1/*
2 * Copyright (c) 2001-2007 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License").  You may not use this file except in compliance with the
9 * License.  Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
11 *
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22
23
24#include <stdio.h>
25#include <stdlib.h>
26#include <unistd.h>
27#include <getopt.h>
28#include <sys/fcntl.h>
29
30#include <mach/mach.h>
31
32#include <CoreFoundation/CoreFoundation.h>
33
34#include <IOKit/IOKitLib.h>
35#include <IOKit/IOCFUnserialize.h>
36#include <IOKit/IOMessage.h>
37
38#include <MediaKit/MKMedia.h>
39#include <MediaKit/MKMediaAccess.h>
40
41#include "AppleRAIDUserLib.h"
42#include "AppleRAIDMember.h"   // for V2 header
43
44#define AUTO_YES	1
45#define AUTO_NO		2
46
47static int		autoRebuild = 0;
48static UInt64		blockSize = 0;
49static bool		extents = false;
50static char *		hint = 0;
51static char *		nickname = 0;
52static int		quickRebuild = 0;
53static UInt64		timeout = 0;
54static bool		verbose = false;
55static UInt64		volSize = 0;
56
57static void
58usage()
59{
60    printf("\n");
61    printf("usage:\n");
62    printf("\n");
63    printf("artest --list\n");
64    printf("artest --lvlist [--extents] <lv uuid | lvg uuid>\n");
65    printf("\n");
66    printf("artest --create --name <nickname> --level <level> <options> disk1s3 disk2s3 disk3s3 ...\n");
67    printf("artest --destroy <set uuid>\n");
68    printf("\n");
69    printf("artest --add <set uuid> disk1s3 ...\n");
70    printf("artest --spare <set uuid> disk1s3 ...\n");
71    printf("artest --remove <set uuid> <member uuid> ...\n");
72    printf("artest --modify <set uuid> <options>\n");
73    printf("\n");
74    printf("artest --lvcreate <lvg uuid> <lvoptions>\n");
75    printf("artest --lvdestroy <lv uuid>\n");
76    printf("\n");
77    printf("artest --lvmodify <lv uuid> <lvoptions>\n");
78    printf("artest --lvresize <lv uuid> [--size <number>]\n");
79    printf("artest --lvsnap <lv uuid> [--size <number>] --level=\"snap ro\"|\"snap rw\">\n");
80    printf("\n");
81    printf("artest --erase disk1s3 disk2s3 disk3s3 ...\n");
82    printf("artest --header disk1s3 disk2s3 disk3s3 ...\n");
83    printf("\n");
84    printf("parameters:\n");
85    printf("	<uuid> = XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX\n");
86    printf("	<nickname> = \"the volume name\"\n");
87    printf("	<level> = stripe, mirror, concat, lvg\n");
88    printf("	<hint> = Apple_HFS, RAIDNoMedia, RAIDNoFS\n");
89    printf("	<options> = [--auto-rebuild=Yes,No] [--block-size=0x8000] [--hint=<hint>]\n");
90    printf("	<options> = [--name=<nickname>] [--timeout=30] [--quick-rebuild=Yes]\n");
91    printf("	<lvoptions> = [--level=concat] [--hint=<hint>] [--name=<nickname>] [--size 0xXXXXXXXXXXXXXXXX]\n");
92    printf("\n");
93    printf("global options:\n");
94    printf("	--verbose\n");
95    printf("    --watch (also a command)\n");
96}
97
98
99// there must be something like this already?
100
101static void
102CFPrintf(CFStringRef format, ...)
103{
104    CFStringRef cfstring;
105    va_list argList;
106
107    va_start(argList, format);
108    cfstring = CFStringCreateWithFormatAndArguments(NULL, NULL, format, argList);
109    va_end(argList);
110
111    CFIndex cfstringSize = CFStringGetLength(cfstring);
112    CFIndex stringSize = CFStringGetMaximumSizeForEncoding(cfstringSize, kCFStringEncodingUTF8) + 1;
113    char *string = malloc(stringSize);
114    if (CFStringGetCString(cfstring, string, stringSize, kCFStringEncodingUTF8)) {
115	printf("%s", string);
116    }
117    free(string);
118}
119
120
121#define PMROptions	(PMEXTENDEDMODE | PMSECTORIZE | PMSORTMAP)
122
123#define	kRAID_ONLINE	"Apple_RAID"
124#define	kRAID_OFFLINE	"Apple_RAID_Offline"
125
126// switches partition type
127
128static bool
129switchPartition(char * diskName, char * partitionType)
130{
131    char wholeDevicePath[256];
132    sprintf(wholeDevicePath, "/dev/%s", diskName);
133
134    unsigned int partitionNumber = 0;
135    char * c = wholeDevicePath + 5 + 4;		        // skip over "/dev/disk"
136    while (*c != 's' && *c++);				// look for 's'
137    if (*c == 's') {
138	*c = 0;						// clip off remainder
139	sscanf(c+1, "%u", &partitionNumber);		// get partition number
140    }
141    if (!partitionNumber) return true;			// just assume it is a raid disk
142
143#define LIVERAID
144#ifdef LIVERAID
145    char * optionString = "<dict> <key>Writable</key> <true/> <key>Shared Writer</key> <true/></dict>";
146#else
147    char * optionString = "<dict> <key>Writable</key> <true/> </dict>";
148#endif
149    CFDictionaryRef options = IOCFUnserialize(optionString, kCFAllocatorDefault, 0, NULL);
150    if (!options) exit(1);
151
152    int32_t err;
153    MKMediaRef device = MKMediaCreateWithPath(nil, wholeDevicePath, options, &err);
154    CFRelease(options);
155    if (!device || err) return false;
156
157    options = NULL;
158    MKStatus err2;
159    CFMutableDictionaryRef media = MKCFReadMedia(options, device, &err2);
160    if (!media || err2) goto Failure;
161
162    // find and extract the 'Schemes' array
163    CFMutableArrayRef Schemes = (CFMutableArrayRef) CFDictionaryGetValue(media, CFSTR("Schemes"));
164    if (!Schemes) goto Failure;
165
166    // DMTool just grabs the first "default" scheme, so do the same
167    CFMutableDictionaryRef Scheme = (CFMutableDictionaryRef) CFArrayGetValueAtIndex(Schemes, 0);
168    if (!Scheme) goto Failure;
169
170    // Then find and extract the 'Sections' array of that scheme:
171    CFMutableArrayRef Sections = (CFMutableArrayRef) CFDictionaryGetValue(Scheme, CFSTR("Sections"));
172    if (!Sections) goto Failure;
173
174    // Every scheme can have multiple sections to it, we need to find the 'MAP' section:
175    CFMutableDictionaryRef Section = (CFMutableDictionaryRef) CFArrayDictionarySearch(Sections, CFSTR("ID"), CFSTR("MAP"));
176    if (!Section) goto Failure;
177
178    // Then find and extract the 'Partitions' array of that section:
179    CFMutableArrayRef Partitions = (CFMutableArrayRef) CFDictionaryGetValue(Section, CFSTR("Partitions"));
180    if (!Partitions) goto Failure;
181
182    CFNumberRef partitionIndex = CFNumberCreate(nil, kCFNumberSInt32Type, &partitionNumber);
183    if (!partitionIndex) goto Failure;
184    CFMutableDictionaryRef Partition = (CFMutableDictionaryRef) CFArrayDictionarySearch(Partitions, CFSTR("Partition ID"), partitionIndex);
185    if (!Partition) goto Failure;
186
187    // change the partition type (finally!)
188    CFStringRef Type = CFStringCreateWithCString(nil, partitionType, kCFStringEncodingUTF8);
189    if (!Type) goto Failure;
190    CFDictionarySetValue(Partition, CFSTR("Type"), Type);
191
192    CFMutableDictionaryRef woptions = CFDictionaryCreateMutable(kCFAllocatorDefault,
193								2,
194								&kCFTypeDictionaryKeyCallBacks,
195								&kCFTypeDictionaryValueCallBacks);
196    if (!woptions) goto Failure;
197    CFDictionarySetValue(woptions, CFSTR("Retain existing content"), kCFBooleanTrue);
198    CFDictionarySetValue(woptions, CFSTR("Direct Mode"), kCFBooleanTrue);
199
200    err2 = MKCFWriteMedia(media, nil, nil, woptions, device);
201
202    MKCFDisposeMedia(media);
203    CFRelease(woptions);
204    CFRelease(device);
205    return !err2;
206
207Failure:
208    if (media) MKCFDisposeMedia(media);
209    if (device) CFRelease(device);
210
211    return false;
212}
213
214static void
215addMember(char * uuid, CFStringRef type, int argc, char* argv[])
216{
217    if (argc < 1) {
218	usage();
219	exit(1);
220    }
221
222    CFStringRef setUUID = CFStringCreateWithCString(kCFAllocatorDefault, uuid, kCFStringEncodingUTF8);
223    if (!setUUID) exit(1);
224
225    CFMutableDictionaryRef setInfo = AppleRAIDGetSetProperties(setUUID);
226    if (!setInfo) {
227	printf("addMember - failed to find RAID set \"%s\"\n", uuid);
228	exit(1);
229    }
230
231    int partitionCount = argc;
232    char **firstPartition = argv;
233
234    while (argc--) {
235	printf("adding partition \"%s\"\n", *argv);
236	CFStringRef partitionName = CFStringCreateWithCString(kCFAllocatorDefault, *argv, kCFStringEncodingUTF8);
237
238	bool success = switchPartition(*argv, kRAID_OFFLINE);
239	if (!success) {
240	    printf("switching the partition on \"%s\" to %s FAILED.\n", *argv, kRAID_OFFLINE);
241	    exit(1);
242	}
243
244	AppleRAIDMemberRef member = AppleRAIDAddMember(setInfo, partitionName, type);
245	if (!member) {
246	    printf("addMember - there was problem adding partition \"%s\"\n", *argv);
247	    exit(1);
248	}
249
250	argv++;
251    }
252
253    printf("updating set \"%s\".\n", uuid);
254    AppleRAIDSetRef set = AppleRAIDUpdateSet(setInfo);
255    CFRelease(setInfo);
256
257    if (!set) {
258	printf("something went wrong adding members to the set\n");
259	exit(1);
260    }
261
262    while (partitionCount--) {
263	printf("switching the partition on \"%s\" to %s.\n", *firstPartition, kRAID_ONLINE);
264	bool success = switchPartition(*firstPartition, kRAID_ONLINE);
265	if (!success) {
266	    printf("switching the partition on \"%s\" to %s FAILED.\n", *firstPartition, kRAID_ONLINE);
267	    exit(1);
268	}
269
270	firstPartition++;
271    }
272
273    // at this point the code should wait for notifications that members and the set are available
274}
275
276static void
277createSet(char * levelCString, char * nameCString, int argc, char* argv[])
278{
279    if (!levelCString || !nameCString || argc < 2) {
280	usage();
281	exit(1);
282    }
283
284    CFStringRef level = CFStringCreateWithCString(kCFAllocatorDefault, levelCString, kCFStringEncodingUTF8);
285    CFStringRef name = CFStringCreateWithCString(kCFAllocatorDefault, nameCString, kCFStringEncodingUTF8);
286    if (!level || !name) exit(1);
287
288    // get list of available raid set descriptions
289    CFMutableArrayRef descriptionArray = AppleRAIDGetSetDescriptions();
290
291    CFIndex dCount = CFArrayGetCount(descriptionArray);
292    CFIndex dIndex = 0;
293    CFStringRef dLevel = 0;
294    for (dIndex = 0; dIndex < dCount; dIndex++) {
295	CFMutableDictionaryRef setDescription = (CFMutableDictionaryRef)CFArrayGetValueAtIndex(descriptionArray, dIndex);
296	dLevel = (CFStringRef)CFDictionaryGetValue(setDescription, CFSTR(kAppleRAIDLevelNameKey));
297	if (!dLevel) break;
298
299	if (CFStringCompare(dLevel, level, kCFCompareCaseInsensitive) == kCFCompareEqualTo) break;
300	dLevel = 0;
301    }
302    if (dLevel == 0) {
303	printf("raid level \"%s\" is not valid?.\n", levelCString);
304	exit(1);
305    }
306
307    CFMutableDictionaryRef setInfo = AppleRAIDCreateSet(dLevel, name);
308    if (!setInfo) exit(1);
309
310    char **firstPartition = argv;
311    int partitionCount = argc;
312
313    while (argc--) {
314	printf("adding partition \"%s\"\n", *argv);
315	CFStringRef partitionName = CFStringCreateWithCString(kCFAllocatorDefault, *argv, kCFStringEncodingUTF8);
316
317	AppleRAIDMemberRef member = AppleRAIDAddMember(setInfo, partitionName, CFSTR(kAppleRAIDMembersKey));
318	if (!member) {
319	    printf("there was problem adding partition \"%s\"\n", *argv);
320	    exit(1);
321	}
322	CFShow(member);
323
324	bool success = switchPartition(*argv, kRAID_OFFLINE);
325	if (!success) {
326	    printf("switching the partition on \"%s\" to %s FAILED.\n", *argv, kRAID_OFFLINE);
327	    exit(1);
328	}
329
330	argv++;
331    }
332
333    if (autoRebuild == AUTO_YES) AppleRAIDModifySet(setInfo, CFSTR(kAppleRAIDSetAutoRebuildKey), (void *)kCFBooleanTrue);
334    if (quickRebuild == AUTO_YES) AppleRAIDModifySet(setInfo, CFSTR(kAppleRAIDSetQuickRebuildKey), (void *)kCFBooleanTrue);
335    if (blockSize) {
336	CFNumberRef blockSizeCF = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt64Type, &blockSize);
337	if (blockSizeCF) AppleRAIDModifySet(setInfo, CFSTR(kAppleRAIDChunkSizeKey), (void *)blockSizeCF);
338    }
339    if (hint) {
340	CFStringRef hintCF = CFStringCreateWithCString(kCFAllocatorDefault, hint, kCFStringEncodingUTF8);
341	if (hintCF) AppleRAIDModifySet(setInfo, CFSTR(kAppleRAIDSetContentHintKey), (void *)hintCF);
342    }
343    if (timeout) {
344	CFNumberRef timeoutCF = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt64Type, &timeout);
345	if (timeoutCF) AppleRAIDModifySet(setInfo, CFSTR(kAppleRAIDSetTimeoutKey), (void *)timeoutCF);
346    }
347
348    printf("creating the set \"%s\".\n", nameCString);
349    AppleRAIDSetRef set = AppleRAIDUpdateSet(setInfo);
350
351    CFPrintf(CFSTR("created set %@\n"), set);
352    CFRelease(setInfo);
353
354    if (!set) {
355	printf("something went wrong creating the set\n");
356	exit(0);
357    }
358
359    while (partitionCount--) {
360	printf("switching the partition on \"%s\" to %s.\n", *firstPartition, kRAID_ONLINE);
361	bool success = switchPartition(*firstPartition, kRAID_ONLINE);
362	if (!success) {
363	    printf("switching the partition on \"%s\" to %s FAILED.\n", *firstPartition, kRAID_ONLINE);
364	    exit(1);
365	}
366
367	firstPartition++;
368    }
369
370    // at this point the code should wait for notifications that members and the set are available
371}
372
373static void
374destroySet(char * nameCString, int argc, char* argv[])
375{
376    if (!nameCString || argc) {
377	usage();
378	exit(1);
379    }
380
381    CFStringRef setUUID = CFStringCreateWithCString(kCFAllocatorDefault, nameCString, kCFStringEncodingUTF8);
382    if (!setUUID) exit(1);
383
384    bool success = AppleRAIDDestroySet(setUUID);
385    if (!success) {
386	printf("there was a problem destroying the set %s.\n", nameCString);
387    }
388}
389
390
391static void
392erasePartition(int argc, char* argv[])
393{
394    if (argc < 1) {
395	usage();
396	exit(1);
397    }
398
399    while (argc--) {
400
401	printf("switching the partition type on \"%s\" to %s.\n", *argv, kRAID_OFFLINE);
402	bool success = switchPartition(*argv, kRAID_OFFLINE);
403	if (!success) {
404	    printf("switching partition type FAILED.\n");
405	}
406
407	printf("erasing raid headers on partition \"%s\"\n", *argv);
408	CFStringRef partitionName = CFStringCreateWithCString(kCFAllocatorDefault, *argv, kCFStringEncodingUTF8);
409	success = AppleRAIDRemoveHeaders(partitionName);
410	if (!success) {
411	    printf("erasing the raid headers on partition \"%s\" FAILED.\n", *argv);
412	}
413
414	argv++;
415    }
416}
417
418static void
419dumpHeader(int argc, char* argv[])
420{
421    if (argc < 1) {
422	usage();
423	exit(1);
424    }
425
426    while (argc--) {
427
428//	printf("dumping the raid header on \"%s\".\n", *argv);
429
430	CFStringRef partitionName = CFStringCreateWithCString(kCFAllocatorDefault, *argv, kCFStringEncodingUTF8);
431	CFDataRef data = AppleRAIDDumpHeader(partitionName);
432
433	if (data) {
434	    AppleRAIDHeaderV2 * header = (AppleRAIDHeaderV2 *)CFDataGetBytePtr(data);
435	    if (header) printf("%s\n", header->plist);
436	}
437
438	argv++;
439    }
440}
441
442static void
443dumpSetProperties(CFMutableDictionaryRef set)
444{
445    CFPrintf(CFSTR("\n%@\n"), CFDictionaryGetValue(set, CFSTR(kAppleRAIDSetUUIDKey)));
446    CFPrintf(CFSTR("\t\"%@\" type = %@ /dev/%@\n"),
447	     CFDictionaryGetValue(set, CFSTR(kAppleRAIDSetNameKey)),
448	     CFDictionaryGetValue(set, CFSTR(kAppleRAIDLevelNameKey)),
449	     CFDictionaryGetValue(set, CFSTR("BSD Name")));
450    CFPrintf(CFSTR("\tstatus = %@, sequence = %@\n"),
451	     CFDictionaryGetValue(set, CFSTR(kAppleRAIDStatusKey)),
452	     CFDictionaryGetValue(set, CFSTR(kAppleRAIDSequenceNumberKey)));
453    CFPrintf(CFSTR("\tchunk count = %@, chunk size = %@\n"),
454	     CFDictionaryGetValue(set, CFSTR(kAppleRAIDChunkCountKey)),
455	     CFDictionaryGetValue(set, CFSTR(kAppleRAIDChunkSizeKey)));
456
457    CFStringRef level = CFDictionaryGetValue(set, CFSTR(kAppleRAIDLevelNameKey));
458    if (CFStringCompare(level, CFSTR("mirror"), kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
459	CFPrintf(CFSTR("\tcontent hint = %@, auto = %@, quick = %@, timeout = %@\n"),
460		 CFDictionaryGetValue(set, CFSTR(kAppleRAIDSetContentHintKey)),
461		 CFDictionaryGetValue(set, CFSTR(kAppleRAIDSetAutoRebuildKey)),
462		 CFDictionaryGetValue(set, CFSTR(kAppleRAIDSetQuickRebuildKey)),
463		 CFDictionaryGetValue(set, CFSTR(kAppleRAIDSetTimeoutKey)));
464    } else if (CFStringCompare(level, CFSTR("LVG"), kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
465	CFPrintf(CFSTR("\tcontent hint = %@, lv count = %@, free space %@\n"),
466		 CFDictionaryGetValue(set, CFSTR(kAppleRAIDSetContentHintKey)),
467		 CFDictionaryGetValue(set, CFSTR(kAppleRAIDLVGVolumeCountKey)),
468		 CFDictionaryGetValue(set, CFSTR(kAppleRAIDLVGFreeSpaceKey)));
469    } else {
470	CFPrintf(CFSTR("\tcontent hint = %@\n"), CFDictionaryGetValue(set, CFSTR(kAppleRAIDSetContentHintKey)));
471    }
472
473    if (verbose) CFShow(set);
474}
475
476static void
477dumpMemberProperties(CFMutableDictionaryRef member)
478{
479    CFPrintf(CFSTR("\t%@\n"), CFDictionaryGetValue(member, CFSTR(kAppleRAIDMemberUUIDKey)));
480    CFPrintf(CFSTR("\t\tmember index = %@, sequence = %@, /dev/%@\n"),
481	     CFDictionaryGetValue(member, CFSTR(kAppleRAIDMemberIndexKey)),
482	     CFDictionaryGetValue(member, CFSTR(kAppleRAIDSequenceNumberKey)),
483	     CFDictionaryGetValue(member, CFSTR("BSD Name")));
484    CFPrintf(CFSTR("\t\tstatus = %@, chunk count = %@, rebuild = %@\n"),
485	     CFDictionaryGetValue(member, CFSTR(kAppleRAIDMemberStatusKey)),
486	     CFDictionaryGetValue(member, CFSTR(kAppleRAIDChunkCountKey)),
487	     CFDictionaryGetValue(member, CFSTR(kAppleRAIDRebuildStatus)));
488
489    if (verbose) CFShow(member);
490}
491
492static void
493listRAIDSets()
494{
495    UInt32 filter = kAppleRAIDAllSets;
496    CFMutableArrayRef theList = AppleRAIDGetListOfSets(filter);
497    CFIndex setCount = theList ? CFArrayGetCount(theList) : 0;
498
499    printf("AppleRAIDGetListOfSets found %d sets\n", (int)setCount); fflush(stdout);
500
501    // get each set's properties
502    CFIndex i;
503    for (i=0; i < setCount; i++) {
504
505	CFStringRef setName = (CFStringRef)CFArrayGetValueAtIndex(theList, i);
506	if (setName) {
507	    CFMutableDictionaryRef setProp = AppleRAIDGetSetProperties(setName);
508	    if (!setProp) {
509		CFPrintf(CFSTR("%@\n\t(lookup failed)\n"), setName);
510		continue;
511	    }
512	    dumpSetProperties(setProp);
513
514	    // get each member's properties
515	    bool spares = false;
516	    CFMutableArrayRef members = (CFMutableArrayRef)CFDictionaryGetValue(setProp, CFSTR(kAppleRAIDMembersKey));
517	    CFIndex j, memberCount = members ? CFArrayGetCount(members) : 0;
518
519	    printf("members (%d):\n", (int)memberCount);
520
521	again:
522	    for (j=0; j < memberCount; j++) {
523
524		CFStringRef memberName = (CFStringRef)CFArrayGetValueAtIndex(members, j);
525		if (memberName) {
526		    CFMutableDictionaryRef memberProp = AppleRAIDGetMemberProperties(memberName);
527		    if (memberProp) {
528			dumpMemberProperties(memberProp);
529			CFRelease(memberProp);
530		    } else {
531			CFPrintf(CFSTR("\t%@\n\t\t(lookup failed)\n"), memberName);
532		    }
533		}
534	    }
535
536	    if (!spares) {
537		members = (CFMutableArrayRef)CFDictionaryGetValue(setProp, CFSTR(kAppleRAIDSparesKey));
538		memberCount = members ? CFArrayGetCount(members) : 0;
539		if (memberCount) printf("spares: (%d)\n", (int)memberCount);
540		spares = true;
541		goto again;
542	    }
543	    spares = false;
544	    CFRelease(setProp);
545	}
546    }
547    if (theList) CFRelease(theList);
548}
549
550static void
551modifySet(char * setUUIDCString, int argc, char* argv[])
552{
553    if (!setUUIDCString || argc) {
554	usage();
555	exit(1);
556    }
557
558    CFStringRef setUUID = CFStringCreateWithCString(kCFAllocatorDefault, setUUIDCString, kCFStringEncodingUTF8);
559    if (!setUUID) exit(1);
560
561    CFMutableDictionaryRef setInfo = AppleRAIDGetSetProperties(setUUID);
562    if (!setInfo) {
563	printf("modifySet - failed to find RAID set \"%s\"\n", setUUIDCString);
564	exit(1);
565    }
566
567    if (autoRebuild == AUTO_YES) AppleRAIDModifySet(setInfo, CFSTR(kAppleRAIDSetAutoRebuildKey), (void *)kCFBooleanTrue);
568    if (autoRebuild == AUTO_NO) AppleRAIDModifySet(setInfo, CFSTR(kAppleRAIDSetAutoRebuildKey), (void *)kCFBooleanFalse);
569    if (quickRebuild == AUTO_YES) AppleRAIDModifySet(setInfo, CFSTR(kAppleRAIDSetQuickRebuildKey), (void *)kCFBooleanTrue);
570    if (quickRebuild == AUTO_NO) AppleRAIDModifySet(setInfo, CFSTR(kAppleRAIDSetQuickRebuildKey), (void *)kCFBooleanFalse);
571    if (blockSize) {
572	CFNumberRef blockSizeCF = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt64Type, &blockSize);
573	if (blockSizeCF) AppleRAIDModifySet(setInfo, CFSTR(kAppleRAIDChunkSizeKey), (void *)blockSizeCF);
574    }
575    if (hint) {
576	CFStringRef hintCF = CFStringCreateWithCString(kCFAllocatorDefault, hint, kCFStringEncodingUTF8);
577	if (hintCF) AppleRAIDModifySet(setInfo, CFSTR(kAppleRAIDSetContentHintKey), (void *)hintCF);
578    }
579    if (timeout) {
580	CFNumberRef timeoutCF = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt64Type, &timeout);
581	if (timeoutCF) AppleRAIDModifySet(setInfo, CFSTR(kAppleRAIDSetTimeoutKey), (void *)timeoutCF);
582    }
583    if (nickname) {
584	CFStringRef nicknameCF = CFStringCreateWithCString(kCFAllocatorDefault, nickname, kCFStringEncodingUTF8);
585	if (nicknameCF) AppleRAIDModifySet(setInfo, CFSTR(kAppleRAIDSetNameKey), (void *)nicknameCF);
586    }
587
588    printf("modifying the set \"%s\".\n", setUUIDCString);
589    AppleRAIDSetRef set = AppleRAIDUpdateSet(setInfo);
590
591    if (!set) printf("something went wrong updating the set\n");
592
593    if (set) CFRelease(set);
594    CFRelease(setInfo);
595    CFRelease(setUUID);
596}
597
598static void
599removeMember(char * setUUIDCString, int argc, char* argv[])
600{
601    if (!setUUIDCString || argc < 1) {
602	usage();
603	exit(1);
604    }
605
606    CFStringRef setUUID = CFStringCreateWithCString(kCFAllocatorDefault, setUUIDCString, kCFStringEncodingUTF8);
607    if (!setUUID) exit(1);
608
609    CFMutableDictionaryRef setInfo = AppleRAIDGetSetProperties(setUUID);
610    if (!setInfo) {
611	printf("removeMember - failed to find RAID set \"%s\"\n", setUUIDCString);
612	exit(1);
613    }
614
615    int partitionCount = argc;
616    char **firstPartition = argv;
617
618    while (argc--) {
619	printf("removing member \"%s\"\n", *argv);
620
621	CFStringRef memberUUID = CFStringCreateWithCString(kCFAllocatorDefault, *argv, kCFStringEncodingUTF8);
622
623	bool success = AppleRAIDRemoveMember(setInfo, memberUUID);
624	if (!success) {
625	    printf("there was problem removing the member \"%s\"\n", *argv);
626	}
627
628	CFRelease(memberUUID);
629	argv++;
630    }
631
632    AppleRAIDSetRef set = AppleRAIDUpdateSet(setInfo);
633
634    if (!set) printf("something went wrong updating the set\n");
635
636    if (set) CFRelease(set);
637    CFRelease(setInfo);
638    CFRelease(setUUID);
639
640    while (partitionCount--) {
641	printf("switching the partition on \"%s\" to %s.\n", *firstPartition, kRAID_OFFLINE);
642	bool success = switchPartition(*firstPartition, kRAID_OFFLINE);
643	if (!success) {
644	    printf("switching the partition on \"%s\" to %s FAILED.\n", *firstPartition, kRAID_OFFLINE);
645	}
646
647	firstPartition++;
648    }
649}
650
651static void
652createLogicalVolume(char * nameCString, char * volTypeCString, int argc, char* argv[])
653{
654    if (!nameCString || argc) {
655	usage();
656	exit(1);
657    }
658
659    CFStringRef lvgUUID = CFStringCreateWithCString(kCFAllocatorDefault, nameCString, kCFStringEncodingUTF8);
660    if (!lvgUUID) exit(1);
661
662    if (!volSize) volSize = 0x40000000;
663
664    CFStringRef volType = 0;
665    if (volTypeCString) {
666	volType = CFStringCreateWithCString(kCFAllocatorDefault, volTypeCString, kCFStringEncodingUTF8);
667    } else {
668	volType = CFSTR(kAppleLVMVolumeTypeConcat);
669    }
670    if (!volType) exit(1);
671
672    CFMutableDictionaryRef lvDict = AppleLVMCreateVolume(lvgUUID, volType, volSize, CFSTR(kAppleLVMVolumeLocationFast));
673    if (!lvDict) {
674	printf("there was a problem allocating/setting up the logical volume\n");
675	exit(1);
676    }
677
678    if (nickname) {
679	CFStringRef nameCF = CFStringCreateWithCString(kCFAllocatorDefault, nickname, kCFStringEncodingUTF8);
680	if (nameCF) AppleLVMModifyVolume(lvDict, CFSTR(kAppleLVMVolumeNameKey), (void *)nameCF);
681    }
682
683    if (hint) {
684	CFStringRef hintCF = CFStringCreateWithCString(kCFAllocatorDefault, hint, kCFStringEncodingUTF8);
685	if (hintCF) AppleLVMModifyVolume(lvDict, CFSTR(kAppleLVMVolumeContentHintKey), (void *)hintCF);
686    }
687
688    AppleLVMVolumeRef volRef = AppleLVMUpdateVolume(lvDict);
689    if (!volRef) {
690	printf("there was a problem writing out the logical volume onto the group %s.\n", nameCString);
691	exit(2);
692    }
693}
694
695static void
696destroyLogicalVolume(char * nameCString, int argc, char* argv[])
697{
698    if (!nameCString || argc) {
699	usage();
700	exit(1);
701    }
702
703    CFStringRef lvUUID = CFStringCreateWithCString(kCFAllocatorDefault, nameCString, kCFStringEncodingUTF8);
704    if (!lvUUID) exit(1);
705
706    bool success = AppleLVMDestroyVolume(lvUUID);
707    if (!success) {
708	printf("there was a problem destroying the logical volume %s.\n", nameCString);
709    }
710}
711
712static void
713dumpLogicalVolumeExtents(CFMutableDictionaryRef lv)
714{
715    CFStringRef lvUUID = CFDictionaryGetValue(lv, CFSTR(kAppleLVMVolumeUUIDKey));
716    if (!lvUUID) { printf("\ninternal error, no uuid in lv dict\n"); return; };
717
718    CFDataRef extentData = (CFDataRef)AppleLVMGetVolumeExtents(lvUUID);
719    if (!extentData) { printf("\nno extent data found?\n"); return; };
720
721    AppleRAIDExtentOnDisk * extentList = (AppleRAIDExtentOnDisk *)CFDataGetBytePtr(extentData);
722    UInt64 extentCount = CFDataGetLength(extentData) / sizeof(AppleRAIDExtentOnDisk);
723    if (!extentCount || !extentList) { printf("\nextent data empty?\n"); return; };
724
725    printf("\textent list:\n");
726
727    UInt32 i;
728    for (i = 0; i < extentCount; i++) {
729	printf("  %20llu - %12llu (%llu)\n",
730	       extentList[i].extentByteOffset,
731	       extentList[i].extentByteOffset + extentList[i].extentByteCount - 1,
732	       extentList[i].extentByteCount);
733    }
734}
735
736static void
737dumpLogicalVolumeProperties(CFMutableDictionaryRef lv)
738{
739    CFPrintf(CFSTR("\n%@\n"), CFDictionaryGetValue(lv, CFSTR(kAppleLVMVolumeUUIDKey)));
740    CFPrintf(CFSTR("\t\"%@\" type = %@ /dev/%@\n"),
741	     CFDictionaryGetValue(lv, CFSTR(kAppleLVMVolumeNameKey)),
742	     CFDictionaryGetValue(lv, CFSTR(kAppleLVMVolumeTypeKey)),
743	     CFDictionaryGetValue(lv, CFSTR("BSD Name")));
744    CFPrintf(CFSTR("\tbyte size = %@, content hint = %@\n"),
745	     CFDictionaryGetValue(lv, CFSTR(kAppleLVMVolumeSizeKey)),
746	     CFDictionaryGetValue(lv, CFSTR(kAppleLVMVolumeContentHintKey)));
747    CFPrintf(CFSTR("\tstatus = %@, sequence = %@ extent count = %@\n"),
748	     CFDictionaryGetValue(lv, CFSTR(kAppleLVMVolumeStatusKey)),
749	     CFDictionaryGetValue(lv, CFSTR(kAppleLVMVolumeSequenceKey)),
750	     CFDictionaryGetValue(lv, CFSTR(kAppleLVMVolumeExtentCountKey)));
751    CFStringRef parent = CFDictionaryGetValue(lv, CFSTR(kAppleLVMParentUUIDKey));
752    if (parent) CFPrintf(CFSTR("\tparent = %@\n"), parent);
753
754    if (extents) dumpLogicalVolumeExtents(lv);
755
756    if (verbose) CFShow(lv);
757}
758
759static void listLogicalVolumes(char * nameCString, int argc, char* argv[]);
760
761static void
762listAllLogicalVolumes()
763{
764    UInt32 filter = kAppleRAIDAllSets;
765    CFMutableArrayRef theList = AppleRAIDGetListOfSets(filter);
766    CFIndex setCount = theList ? CFArrayGetCount(theList) : 0;
767
768    CFIndex i;
769    for (i=0; i < setCount; i++) {
770
771	CFStringRef setName = (CFStringRef)CFArrayGetValueAtIndex(theList, i);
772	if (setName) {
773	    CFMutableDictionaryRef setProp = AppleRAIDGetSetProperties(setName);
774	    if (!setProp) continue;
775
776	    CFStringRef level = CFDictionaryGetValue(setProp, CFSTR(kAppleRAIDLevelNameKey));
777	    if (!level) continue;
778
779	    if (CFStringCompare(level, CFSTR("LVG"), kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
780
781		dumpSetProperties(setProp);
782
783		char uuid[256];
784		if (CFStringGetCString(setName, uuid, 256, kCFStringEncodingUTF8)) {
785		    listLogicalVolumes(uuid, 0, NULL);
786		}
787	    }
788
789	    CFRelease(setProp);
790	}
791    }
792    if (theList) CFRelease(theList);
793}
794
795
796static void
797listLogicalVolumes(char * nameCString, int argc, char* argv[])
798{
799    if (!nameCString && !argc) {
800	listAllLogicalVolumes();
801	exit(0);
802    }
803
804    if (!nameCString && argc == 1) nameCString = argv[0];  // hack
805    if (!nameCString) {
806	usage();
807	exit(1);
808    }
809
810    CFStringRef lvUUID = CFStringCreateWithCString(kCFAllocatorDefault, nameCString, kCFStringEncodingUTF8);
811    if (!lvUUID) exit(1);
812
813    // try for one logical volume
814    CFMutableDictionaryRef props = AppleLVMGetVolumeProperties(lvUUID);
815    if (props) {
816	dumpLogicalVolumeProperties(props);
817	CFRelease(props);
818	return;
819    }
820
821    // go for all volumes in the group
822    CFMutableArrayRef volumes = AppleLVMGetVolumesForGroup(lvUUID, NULL);
823    if (!volumes) {
824	printf("there was a problem finding the logical volume/group %s.\n", nameCString);
825	exit(1);
826    }
827
828    int count = CFArrayGetCount(volumes);
829    if (count == 0) {
830	printf("\n%s contains no logical volumes.\n", nameCString);
831	return;
832    }
833
834    int i;
835    for (i = 0; i < count; i++) {
836
837	CFStringRef UUID = (CFStringRef)CFArrayGetValueAtIndex(volumes, i);
838
839	CFMutableDictionaryRef props = AppleLVMGetVolumeProperties(UUID);
840	if (props) {
841	    dumpLogicalVolumeProperties(props);
842	    CFRelease(props);
843	}
844    }
845    printf("\n");
846}
847
848static void
849modifyLogicalVolume(char * nameCString, int argc, char* argv[])
850{
851    if (!nameCString || argc) {
852	usage();
853	exit(1);
854    }
855
856    CFStringRef lvUUID = CFStringCreateWithCString(kCFAllocatorDefault, nameCString, kCFStringEncodingUTF8);
857    if (!lvUUID) exit(1);
858
859    CFMutableDictionaryRef lvDict = AppleLVMGetVolumeProperties(lvUUID);
860    if (!lvDict) {
861	printf("there was a problem finding the logical volume %s.\n", nameCString);
862	exit(1);
863    }
864
865    if (nickname) {
866	CFStringRef nameCF = CFStringCreateWithCString(kCFAllocatorDefault, nickname, kCFStringEncodingUTF8);
867	if (nameCF) AppleLVMModifyVolume(lvDict, CFSTR(kAppleLVMVolumeNameKey), (void *)nameCF);
868    }
869
870    if (hint) {
871	CFStringRef hintCF = CFStringCreateWithCString(kCFAllocatorDefault, hint, kCFStringEncodingUTF8);
872	if (hintCF) AppleLVMModifyVolume(lvDict, CFSTR(kAppleLVMVolumeContentHintKey), (void *)hintCF);
873    }
874
875    AppleLVMVolumeRef volRef = AppleLVMUpdateVolume(lvDict);
876    if (!volRef) {
877	printf("there was a problem updating the logical volume %s.\n", nameCString);
878	exit(2);
879    }
880}
881
882static void
883resizeLogicalVolume(char * nameCString, int argc, char* argv[])
884{
885    if (!nameCString || argc) {
886	usage();
887	exit(1);
888    }
889
890    CFStringRef lvUUID = CFStringCreateWithCString(kCFAllocatorDefault, nameCString, kCFStringEncodingUTF8);
891    if (!lvUUID) exit(1);
892
893    CFMutableDictionaryRef lvProps = AppleLVMGetVolumeProperties(lvUUID);
894    if (!lvProps) {
895	printf("there was a problem finding the logical volume %s.\n", nameCString);
896	exit(1);
897    }
898
899    if (!volSize) {
900	CFNumberRef number = (CFNumberRef)CFDictionaryGetValue(lvProps, CFSTR(kAppleLVMVolumeSizeKey));
901	if (number) CFNumberGetValue(number, kCFNumberSInt64Type, &volSize);
902	volSize += 0x40000000;
903    }
904
905    UInt64 newSize = AppleLVMResizeVolume(lvProps, volSize);
906    if (!newSize) {
907	printf("there was a problem resizing the logical volume %s.\n", nameCString);
908	exit(1);
909    }
910
911    AppleLVMVolumeRef volRef = AppleLVMUpdateVolume(lvProps);
912    if (!volRef) {
913	printf("there was a problem updating the logical volume %s.\n", nameCString);
914	exit(2);
915    }
916
917    printf("the logical volume %s has been resized to 0x%lld.\n", nameCString, newSize);
918}
919
920static void
921snapshotLogicalVolume(char * nameCString, char * snapType, int argc, char* argv[])
922{
923    if (!nameCString || argc) {
924	usage();
925	exit(1);
926    }
927
928    CFStringRef lvUUID = CFStringCreateWithCString(kCFAllocatorDefault, nameCString, kCFStringEncodingUTF8);
929    if (!lvUUID) exit(1);
930
931    CFMutableDictionaryRef lvProps = AppleLVMGetVolumeProperties(lvUUID);
932    if (!lvProps) {
933	printf("there was a problem finding the logical volume %s.\n", nameCString);
934	exit(1);
935    }
936
937    CFStringRef lvType = 0;
938    if (snapType) {
939	lvType = CFStringCreateWithCString(kCFAllocatorDefault, snapType, kCFStringEncodingUTF8);
940    } else {
941	lvType = CFSTR(kAppleLVMVolumeTypeSnapRO);
942    }
943    if (!lvType) exit(1);
944
945    UInt64 percent = 0;
946    if (volSize < 100) percent = volSize;
947    if (volSize <= 100) volSize = 0;
948
949    if (!volSize) {
950	CFNumberRef number = (CFNumberRef)CFDictionaryGetValue(lvProps, CFSTR(kAppleLVMVolumeSizeKey));
951	if (number) CFNumberGetValue(number, kCFNumberSInt64Type, &volSize);
952    }
953    if (percent) volSize = volSize * percent / (UInt64)100;
954
955    // create the snap
956    CFMutableDictionaryRef snapProps = AppleLVMSnapShotVolume(lvProps, lvType, volSize);
957    if (!snapProps) {
958	printf("there was a problem initializing the snapshot for %s.\n", nameCString);
959	exit(1);
960    }
961
962    // write to disk
963    AppleLVMVolumeRef snapRef = AppleLVMUpdateVolume(snapProps);
964    if (!snapRef) {
965	printf("there was a problem updating the snapshot volume.\n");
966	exit(2);
967    }
968
969    printf("the logical volume %s now has a snapshot.\n", nameCString);
970}
971
972static void
973callBack(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo)
974{
975    char setName[128];
976    if (!CFStringGetCString(object, setName, 128, kCFStringEncodingUTF8)) bcopy("bogus set name?", setName, 128);
977
978    char event[128];
979    if (!CFStringGetCString(name, event, 128, kCFStringEncodingUTF8)) bcopy("bogus event string?", event, 128);
980
981    printf("Notification for %s, event = %s.\n", setName, event); fflush(stdout);
982}
983
984static void signalHandler(int sigraised);
985
986int
987main(int argc, char* argv[])
988{
989    bool add = false, create = false, destroy = false, erase = false, header = false;
990    bool list = false, lvcreate = false, lvdestroy = false, lvlist = false, lvmodify = false, lvresize = false, lvsnap = false;
991    bool modify = false, remove = false, spare = false, watch = false;
992    char * setLevel = 0, * setName = 0;
993
994    /* options descriptor */
995    static struct option longopts[] = {
996	{ "add",	required_argument,	0,	'a' },
997	{ "create",	no_argument,		0,	'c' },
998	{ "destroy",	required_argument,	0,	'd' },
999	{ "erase",	no_argument,		0,	'e' },
1000	{ "header",	no_argument,		0,	'h' },
1001	{ "list",	no_argument,		0,	'l' },
1002	{ "modify",	required_argument,	0,	'm' },
1003	{ "remove",	required_argument,	0,	'r' },
1004	{ "spare",	required_argument,	0,	's' },
1005	{ "watch",	no_argument,		0,	'w' },
1006
1007	{ "lvcreate",	required_argument,	0,	'C' },
1008	{ "lvdestroy",	required_argument,	0,	'D' },
1009	{ "lvlist",	no_argument,		0,	'L' },
1010	{ "lvmodify",	required_argument,	0,	'M' },
1011	{ "lvresize",	required_argument,	0,	'R' },
1012	{ "lvsnap",	required_argument,	0,	'S' },
1013
1014	{ "auto-rebuild",required_argument,	0,	'A' },
1015	{ "block-size", required_argument,	0,	'B' },
1016	{ "extents",	no_argument,		0,	'E' },
1017	{ "hint",	required_argument,	0,	'H' },
1018	{ "level",	required_argument,	0,	'V' },
1019	{ "name",	required_argument,	0,	'N' },
1020	{ "quick-rebuild",required_argument,	0,	'Q' },
1021	{ "size",	required_argument,	0,	'Z' },
1022	{ "timeout",	required_argument,	0,	'T' },
1023
1024	{ "verbose",	no_argument,		0,	'v' },
1025	{ "help",	no_argument,		0,	'?' },
1026	{ 0,		0,			0,	0   }
1027    };
1028
1029    int ch;
1030    while ((ch = getopt_long(argc, argv, "a:cd:ehlm:r:s:wC:D:LM:R:S:A:B:EH:V:N:Q:Z:T:v?", longopts, NULL)) != -1) {
1031
1032	switch(ch) {
1033
1034	case 'a':
1035	    add = true;
1036	    setName = strdup(optarg);
1037	    break;
1038	case 'c':
1039	    create = true;
1040	    break;
1041	case 'd':
1042	    destroy = true;
1043	    setName = strdup(optarg);
1044	    break;
1045	case 'e':
1046	    erase = true;
1047	    break;
1048	case 'h':
1049	    header = true;
1050	    break;
1051	case 'l':
1052	    list = true;
1053	    break;
1054	case 'm':
1055	    modify = true;
1056	    setName = strdup(optarg);
1057	    break;
1058	case 'r':
1059	    remove = true;
1060	    setName = strdup(optarg);
1061	    break;
1062	case 's':
1063	    spare = true;
1064	    setName = strdup(optarg);
1065	    break;
1066	case 'w':
1067	    watch = true;
1068	    break;
1069
1070
1071	case 'C':
1072	    lvcreate = true;
1073	    setName = strdup(optarg);
1074	    break;
1075	case 'D':
1076	    lvdestroy = true;
1077	    setName = strdup(optarg);
1078	    break;
1079	case 'L':
1080	    lvlist = true;
1081	    break;
1082	case 'M':
1083	    lvmodify = true;
1084	    setName = strdup(optarg);
1085	    break;
1086	case 'R':
1087	    lvresize = true;
1088	    setName = strdup(optarg);
1089	    break;
1090	case 'S':
1091	    lvsnap = true;
1092	    setName = strdup(optarg);
1093	    break;
1094
1095
1096	case 'A':
1097	    autoRebuild = ((optarg[0] == 'Y') || (optarg[0] == 'y')) ? AUTO_YES : AUTO_NO;
1098	    break;
1099	case 'B':
1100	    sscanf(optarg, "%lli", &blockSize);
1101	    break;
1102	case 'E':
1103	    extents = true;
1104	    break;
1105	case 'H':
1106	    hint = strdup(optarg);
1107	    break;
1108	case 'V':
1109	    setLevel = strdup(optarg);
1110	    break;
1111	case 'N':
1112	    nickname = strdup(optarg);
1113	    break;
1114	case 'Q':
1115	    quickRebuild = ((optarg[0] == 'Y') || (optarg[0] == 'y')) ? AUTO_YES : AUTO_NO;
1116	    break;
1117	case 'Z':
1118	    sscanf(optarg, "%lli", &volSize);
1119	    break;
1120	case 'T':
1121	    sscanf(optarg, "%lli", &timeout);
1122	    break;
1123
1124
1125	case 'v':
1126	    verbose = true;
1127	    break;
1128	case 0:
1129	case '?':
1130	default:
1131	    usage();
1132	    exit(0);
1133	}
1134    }
1135    argc -= optind;
1136    argv += optind;
1137
1138    if (!add && !create && !destroy && !erase && !header && !list && !modify && !remove && !spare && !watch &&
1139	!lvcreate && !lvdestroy && !lvlist && !lvmodify && !lvresize && !lvsnap) {
1140	usage();
1141	exit(0);
1142    }
1143
1144    if (list) {
1145	listRAIDSets();
1146	exit(0);
1147    }
1148
1149    if (lvlist) {
1150	listLogicalVolumes(NULL, argc, argv);
1151	exit(0);
1152    }
1153
1154    if (geteuid()) {
1155	printf("ERROR: you must be super user for this operation.\n");
1156	exit(1);
1157    }
1158
1159    if (erase) {
1160	erasePartition(argc, argv);
1161	exit(0);
1162    };
1163
1164    if (header) {
1165	dumpHeader(argc, argv);
1166	exit(0);
1167    };
1168
1169    if (watch) {
1170
1171	CFNotificationCenterAddObserver(CFNotificationCenterGetLocalCenter(),
1172					NULL,					// const void *observer
1173					callBack,
1174					CFSTR(kAppleRAIDNotificationSetDiscovered),
1175					NULL,					// const void *object
1176					CFNotificationSuspensionBehaviorHold);
1177
1178	CFNotificationCenterAddObserver(CFNotificationCenterGetLocalCenter(),
1179					NULL,					// const void *observer
1180					callBack,
1181					CFSTR(kAppleRAIDNotificationSetTerminated),
1182					NULL,					// const void *object
1183					CFNotificationSuspensionBehaviorHold);
1184
1185	CFNotificationCenterAddObserver(CFNotificationCenterGetLocalCenter(),
1186					NULL,					// const void *observer
1187					callBack,
1188					CFSTR(kAppleRAIDNotificationSetChanged),
1189					NULL,					// const void *object
1190					CFNotificationSuspensionBehaviorHold);
1191
1192	CFNotificationCenterAddObserver(CFNotificationCenterGetLocalCenter(),
1193					NULL,					// const void *observer
1194					callBack,
1195					CFSTR(kAppleLVMNotificationVolumeDiscovered),
1196					NULL,					// const void *object
1197					CFNotificationSuspensionBehaviorHold);
1198
1199	CFNotificationCenterAddObserver(CFNotificationCenterGetLocalCenter(),
1200					NULL,					// const void *observer
1201					callBack,
1202					CFSTR(kAppleLVMNotificationVolumeTerminated),
1203					NULL,					// const void *object
1204					CFNotificationSuspensionBehaviorHold);
1205
1206	CFNotificationCenterAddObserver(CFNotificationCenterGetLocalCenter(),
1207					NULL,					// const void *observer
1208					callBack,
1209					CFSTR(kAppleLVMNotificationVolumeChanged),
1210					NULL,					// const void *object
1211					CFNotificationSuspensionBehaviorHold);
1212
1213	// this will not fail if there is no raid controller, ie, if AppleRAID class is not instantiated in the kernel
1214
1215	AppleRAIDEnableNotifications();
1216    }
1217
1218
1219    if (add) addMember(setName, CFSTR(kAppleRAIDMembersKey), argc, argv);
1220    if (create) createSet(setLevel, nickname, argc, argv);
1221    if (destroy) destroySet(setName, argc, argv);
1222    if (modify) modifySet(setName, argc, argv);
1223    if (remove) removeMember(setName, argc, argv);
1224    if (spare) addMember(setName, CFSTR(kAppleRAIDSparesKey), argc, argv);
1225
1226
1227    if (lvcreate) createLogicalVolume(setName, setLevel, argc, argv);
1228    if (lvdestroy) destroyLogicalVolume(setName, argc, argv);
1229    if (lvmodify) modifyLogicalVolume(setName, argc, argv);
1230    if (lvresize) resizeLogicalVolume(setName, argc, argv);
1231    if (lvsnap) snapshotLogicalVolume(setName, setLevel, argc, argv);
1232
1233
1234    if (watch) {
1235
1236	printf("watching...\n");
1237
1238	// Set up a signal handler so we can clean up when we're interrupted from the command line
1239	// Otherwise we stay in our run loop forever.
1240	sig_t oldHandler = signal(SIGINT, signalHandler);
1241	if (oldHandler == SIG_ERR) {
1242	    printf("Could not establish new signal handler");
1243	    exit(1);
1244	}
1245
1246	// Start the run loop. Now we'll receive notifications.
1247	//
1248	printf("Starting run loop.\n");
1249	CFRunLoopRun();
1250
1251	printf("Unexpectedly back from CFRunLoopRun()!\n");
1252    }
1253
1254    return 0;
1255}
1256
1257static void
1258signalHandler(int sigraised)
1259{
1260    printf("\nInterrupted.\n");
1261
1262    AppleRAIDDisableNotifications();
1263
1264    _exit(0);
1265}
1266