1 | //␊ |
2 | // StartupPrefPanePref.m␊ |
3 | //␊ |
4 | // Created by Rekursor on 1/16/10.␊ |
5 | //␊ |
6 | ␊ |
7 | #import "ChameleonPrefPane.h"␊ |
8 | ␊ |
9 | #include <process.h>␊ |
10 | #include <property_list.h>␊ |
11 | #include <string>␊ |
12 | ␊ |
13 | //--------------------------------------------------------------------------␊ |
14 | // Constants␊ |
15 | //--------------------------------------------------------------------------␊ |
16 | static const char * const szBootPaths[]= {␊ |
17 | ␉"/",␊ |
18 | ␉"/Extra/",␊ |
19 | ␉"/Volumes/EFI/Extra/",␊ |
20 | ␉"/Volumes/Cham/Extra/",␊ |
21 | ␉"/Volumes/BootLoaders/Extra/",␊ |
22 | ␉"/Volumes/RX0/Extra/",␊ |
23 | ␉"/Library/Preferences/SystemConfiguration/",␊ |
24 | ␉NULL␊ |
25 | };␊ |
26 | ␊ |
27 | static const char* const szPropFileName = "com.apple.Boot.plist";␊ |
28 | static const char* const kDefaultPartition = "Default Partition";␊ |
29 | static const char* const kHidePartition = "Hide Partition";␊ |
30 | static const char* const kRenamePartition = "Rename Partition";␊ |
31 | //--------------------------------------------------------------------------␊ |
32 | // Static file variables␊ |
33 | //--------------------------------------------------------------------------␊ |
34 | static std::string sCurrentDefaultPartition;␊ |
35 | ␊ |
36 | static PartitionExtractor * partExtractor=NULL;␊ |
37 | static PropertyList * prop = NULL;␊ |
38 | static int currentRowSel = -1;␊ |
39 | //--------------------------------------------------------------------------␊ |
40 | ␊ |
41 | @implementation ChameleonPrefPane␊ |
42 | ␊ |
43 | //--------------------------------------------------------------------------␊ |
44 | /**␊ |
45 | * SFAuthorization delegates␊ |
46 | */␊ |
47 | - (void)authorizationViewDidAuthorize:(SFAuthorizationView *)view {␊ |
48 | [self selectDefaultPartition];␊ |
49 | ␉[self refreshLockStates];␊ |
50 | ␊ |
51 | } ␊ |
52 | ␊ |
53 | //--------------------------------------------------------------------------␊ |
54 | - (void)authorizationViewDidDeauthorize:(SFAuthorizationView *)view {␊ |
55 | [self refreshLockStates];␊ |
56 | }␊ |
57 | ␊ |
58 | //--------------------------------------------------------------------------␊ |
59 | - (void) refreshLockStates␊ |
60 | {␊ |
61 | [mPartitionsTable setEnabled:[self isUnlocked]];␊ |
62 | [mSwapHD01 setEnabled:[self isUnlocked]];␊ |
63 | [mSwapHD02 setEnabled:[self isUnlocked]];␊ |
64 | [mStatusText setEnabled:[self isUnlocked]];␊ |
65 | }␊ |
66 | ␊ |
67 | //--------------------------------------------------------------------------␊ |
68 | - (BOOL)isUnlocked ␊ |
69 | {␊ |
70 | return [authView authorizationState] == SFAuthorizationViewUnlockedState;␊ |
71 | }␊ |
72 | ␊ |
73 | //--------------------------------------------------------------------------␊ |
74 | - (id) getImageResource: (NSString *) str ofType: (NSString*) sType␊ |
75 | {␊ |
76 | ␉NSImage * img=nil;␊ |
77 | ␉if(!str) return nil;␊ |
78 | ␉NSBundle * b = [NSBundle bundleForClass:[self class]];␊ |
79 | ␉NSString* sRes = [b pathForResource: str ofType:sType ];␊ |
80 | ␉img = [[NSImage alloc] initWithContentsOfFile: sRes];␊ |
81 | ␉return img;␊ |
82 | }␊ |
83 | - (bool) loadOptionsDict␊ |
84 | {␊ |
85 | ␉␊ |
86 | ␉NSBundle * b = [NSBundle bundleForClass:[self class]];␊ |
87 | ␉if(mOptionsPlistPath !=nil)␉[mOptionsDict dealloc];␊ |
88 | ␉mOptionsPlistPath = [[NSString alloc] initWithString: [b pathForResource: @"Data" ofType:@"plist"]];␊ |
89 | ␉NSMutableDictionary * d = [NSMutableDictionary dictionaryWithContentsOfFile: mOptionsPlistPath];␊ |
90 | ␉if(mOptionsDict !=nil)␉[mOptionsDict dealloc];␊ |
91 | ␉mOptionsDict = [[NSMutableDictionary alloc] initWithCapacity:64 ];␊ |
92 | ␉[mOptionsDict addEntriesFromDictionary: d];␊ |
93 | ␉␊ |
94 | ␉if (d != nil) [d dealloc];␊ |
95 | ␉if (b != nil) [b dealloc];␊ |
96 | ␊ |
97 | ␉return (mOptionsDict != nil) ? true : false;␊ |
98 | }␊ |
99 | //--------------------------------------------------------------------------␊ |
100 | - (id) init␊ |
101 | {␊ |
102 | ␉self = [super init];␊ |
103 | ␊ |
104 | ␉// set the image to display the current file being played␊ |
105 | ␉mMacOSXImage = [self getImageResource: @"MacOSX" ofType: @"png"];␊ |
106 | ␉mWindowsImage = [self getImageResource: @"Windows" ofType: @"png"];␊ |
107 | ␉mLinuxImage = [self getImageResource: @"Linux" ofType: @"png"];␊ |
108 | ␉mCDROMImage = [self getImageResource: @"CDROM" ofType: @"png"];␊ |
109 | ␉mUnknownImage = [self getImageResource: @"Chameleon" ofType: @"tiff"];␊ |
110 | ␉␊ |
111 | ␉␊ |
112 | ␉// Retrieve the Data.plist config␊ |
113 | ␉if (![self loadOptionsDict]) NSLog(@"Error reading plist: %@", mOptionsPlistPath);␊ |
114 | ␉return self;␊ |
115 | }␊ |
116 | ␊ |
117 | //--------------------------------------------------------------------------␊ |
118 | /** When called here, all outlets references are initialized */␊ |
119 | - (void)awakeFromNib␊ |
120 | { // called more than once, we only need one resource init␊ |
121 | ␉static bool ft=true;␊ |
122 | ␉if(ft)␊ |
123 | ␉{␊ |
124 | ␉␉ft=false;␊ |
125 | ␉␉[self initBootConfig];␊ |
126 | ␉}␊ |
127 | }␊ |
128 | ␊ |
129 | //--------------------------------------------------------------------------␊ |
130 | - (void) initBootConfig␊ |
131 | {␊ |
132 | ␉static bool ft=true;␊ |
133 | ␉␊ |
134 | ␉// Cosmetics setup␊ |
135 | ␉// Setup security for changing boot options␊ |
136 | AuthorizationItem items = {kAuthorizationRightExecute, 0, NULL, 0};␊ |
137 | AuthorizationRights rights = {1, &items};␊ |
138 | ␊ |
139 | ␉[authView setAuthorizationRights:&rights];␊ |
140 | authView.delegate = self;␊ |
141 | [authView updateStatus:nil];␊ |
142 | ␊ |
143 | ␉// create the propertylist object that will handle com.apple.Boot.plist␊ |
144 | ␉if(!prop) prop = new PropertyList();␊ |
145 | ␊ |
146 | ␉// create the process that will extract the diskutil list infos␊ |
147 | ␉if(!partExtractor) partExtractor = new PartitionExtractor();␊ |
148 | ␉␊ |
149 | ␉if (!prop->isValid())␊ |
150 | ␉{␊ |
151 | ␉␉std::string sPath;␊ |
152 | ␉␉AuthorizationRef auth = [self isUnlocked] ? [[authView authorization] authorizationRef] : NULL;␊ |
153 | ␉␉CFStringRef errorString=NULL;␊ |
154 | ␉␉bool cont =true;␊ |
155 | ␉␉␊ |
156 | ␉␉id sForcedPath = [mOptionsDict valueForKey: @"forceBootConfigPath"];␊ |
157 | ␉␉const char * szForcedPath = sForcedPath!=nil ? [sForcedPath UTF8String] : NULL;␊ |
158 | ␉␉if (szForcedPath && *szForcedPath)␊ |
159 | ␉␉{␊ |
160 | ␉␉␉cont = !prop->open(szForcedPath, &errorString, auth);␊ |
161 | ␉␉}␊ |
162 | ␉␉else {␊ |
163 | ␉␉␉for(int i=0; szBootPaths[i] && cont; i++)␊ |
164 | ␉␉␉{␊ |
165 | ␉␉␉␉sPath = szBootPaths[i];␊ |
166 | ␉␉␉␉sPath += szPropFileName;␊ |
167 | ␉␉␉␉cont = !prop->open(sPath.c_str(), &errorString, auth);␊ |
168 | ␉␉␉}␊ |
169 | ␉␉}␊ |
170 | ␉␉if (cont)␊ |
171 | ␉␉{␊ |
172 | ␉␉␉if(!ft) return;␊ |
173 | ␉␉␉ft=false;␊ |
174 | ␉␉␉[mStatusText setTextColor: [NSColor redColor] ];␊ |
175 | ␉␉␉if (errorString)␊ |
176 | ␉␉␉␉[mStatusText setStringValue:[NSString stringWithFormat: @"Error while parsing com.apple.Boot.plist : %@",␊ |
177 | ␉␉␉␉␉␉␉␉␉␉␉ errorString] ];␊ |
178 | ␉␉␉else␊ |
179 | ␉␉␉␉[mStatusText setStringValue: @"Error while searching for com.apple.Boot.plist"];␊ |
180 | ␉␉␉␊ |
181 | ␉␉}␊ |
182 | ␉␉else␊ |
183 | ␉␉{ ␊ |
184 | ␉␉␉[mStatusText setTextColor: [NSColor grayColor] ];␊ |
185 | ␉␉␉NSString* ns = [ [NSString alloc] initWithUTF8String:prop->bootConfigPath() ];␊ |
186 | ␉␉␉[mStatusText setStringValue: [NSString stringWithFormat: @"bootConfig: %@", ns] ];␊ |
187 | ␉␉}␊ |
188 | ␉␉if (prop->isValid())␊ |
189 | ␉␉{␊ |
190 | ␉␉␉// read options in the plist file␊ |
191 | ␉␉␉␊ |
192 | ␉␉␉partExtractor->hidePartitions(prop->getStringForKey(kHidePartition));␊ |
193 | ␉␉␉partExtractor->renamedPartitions(prop->getStringForKey(kRenamePartition));␊ |
194 | ␉␉␉␊ |
195 | ␉␉␉// partExtractor->resetSwapping();␊ |
196 | ␉␉␉id val = [mOptionsDict valueForKey:@"swapHD01"];␊ |
197 | ␉␉␉[mSwapHD01 setIntValue: [val intValue] ];␊ |
198 | ␉␉␉[self doSwapHD: [val boolValue] save: false src:0 dst:1];␊ |
199 | ␉␉␉␊ |
200 | ␉␉␉val = [mOptionsDict valueForKey:@"swapHD02"];␊ |
201 | ␉␉␉[mSwapHD02 setIntValue: [val intValue] ];␊ |
202 | ␉␉␉[self doSwapHD: [val boolValue] save: false src:0 dst:2];␊ |
203 | ␉␉␉␊ |
204 | ␉␉␉[self selectDefaultPartition];␊ |
205 | ␉␉}␊ |
206 | ␉␉␊ |
207 | ␉}␉␊ |
208 | }␊ |
209 | //--------------------------------------------------------------------------␊ |
210 | - (void) selectDefaultPartition␊ |
211 | {␊ |
212 | if(!authView) return;␊ |
213 | ␉␊ |
214 | ␉[self refreshLockStates];␊ |
215 | ␉␊ |
216 | ␉// try to get the current default partition if any␊ |
217 | ␉if(partExtractor && prop && prop->isValid())␊ |
218 | ␉{␊ |
219 | ␉␉const char *sdp = prop->getStringForKey(kDefaultPartition);␊ |
220 | ␉␉sCurrentDefaultPartition = sdp ? sdp : ""; ␊ |
221 | ␉␉if (sCurrentDefaultPartition.size())␊ |
222 | ␉␉{␊ |
223 | ␉␉␉int index = partExtractor->getIndexFromHdStringSpec(sCurrentDefaultPartition.c_str());␊ |
224 | ␉␉␉if (index>=0)␊ |
225 | ␉␉␉{␊ |
226 | ␉␉␉␉[mPartitionsTable selectRowIndexes:␊ |
227 | ␉␉␉␉ [NSIndexSet indexSetWithIndex:index] byExtendingSelection:NO];␊ |
228 | ␉␉␉␉currentRowSel = index;␊ |
229 | ␉␉␉}␊ |
230 | ␉␉}␊ |
231 | ␉}␊ |
232 | ␉␊ |
233 | }␊ |
234 | ␊ |
235 | //--------------------------------------------------------------------------␊ |
236 | - (void) swapDisks: (bool) bSwap src: (int) iSrc dst: (int) iDst;␊ |
237 | {␊ |
238 | ␉if(!partExtractor || !prop || !prop->isValid()) return;␊ |
239 | ␉if (bSwap)␊ |
240 | ␉{␊ |
241 | ␉␉partExtractor->swapHD(iSrc, iDst);␉␊ |
242 | ␉}␊ |
243 | ␉partExtractor->extractPartitions();␊ |
244 | ␉[ self selectDefaultPartition];␊ |
245 | ␊ |
246 | }␊ |
247 | ␊ |
248 | //--------------------------------------------------------------------------␊ |
249 | - (void) doSwapHD: (int) val save: (bool) doSave src: (int) isrc dst: (int) idst␊ |
250 | {␊ |
251 | ␉␊ |
252 | ␉if( val>0) //on␊ |
253 | ␉{␊ |
254 | ␉␉[self swapDisks: true src:isrc dst:idst ];␊ |
255 | ␉}␊ |
256 | ␉else␊ |
257 | ␉{␊ |
258 | ␉␉[self swapDisks: false src:isrc dst:idst ];␊ |
259 | ␉}␊ |
260 | ␉␊ |
261 | ␉if(doSave)␊ |
262 | ␉{␊ |
263 | ␉␉if ( [self loadOptionsDict] )␊ |
264 | ␉␉{␊ |
265 | ␉␉␉if (isrc==0 && idst==1)␊ |
266 | ␉␉␉␉[mOptionsDict setObject: [NSNumber numberWithBool: val ? true : false] forKey:@"swapHD01"];␊ |
267 | ␉␉␉if (isrc==0 && idst==2)␊ |
268 | ␉␉␉␉[mOptionsDict setObject: [NSNumber numberWithBool: val ? true : false] forKey:@"swapHD02"];␊ |
269 | ␉␉␉[mOptionsDict writeToFile:mOptionsPlistPath atomically:YES];␊ |
270 | ␉␉}␊ |
271 | ␉}␊ |
272 | ␉␊ |
273 | ␉[mPartitionsTable reloadData];␊ |
274 | ␉[mPartitionsTable scrollRowToVisible: 0];␊ |
275 | ␉//[self tableViewSelectionDidChange: nil];␊ |
276 | ␉␊ |
277 | }␊ |
278 | //--------------------------------------------------------------------------␊ |
279 | - (IBAction)onSwapHD: (id)sender␊ |
280 | {␊ |
281 | ␉partExtractor->resetSwapping();␊ |
282 | ␉[self doSwapHD: !![mSwapHD01 intValue] save:true src:0 dst:1];␊ |
283 | ␉[self doSwapHD: !![mSwapHD02 intValue] save:true src:0 dst:2];␊ |
284 | }␊ |
285 | ␊ |
286 | //--------------------------------------------------------------------------␊ |
287 | // following DieBuch recommendation : using applescript and system events (thanks!):␊ |
288 | - (IBAction)onRestart: (id)sender␊ |
289 | {␊ |
290 | ␉NSInteger n = NSRunAlertPanel(@"Restarting OS X", ␊ |
291 | ␉␉␉␉␉␉␉␉ @"Are you sure you want to restart your computer now ?",␊ |
292 | ␉␉␉␉␉␉␉␉ @"OK", @"Cancel", nil);␊ |
293 | ␉if (n==1)␊ |
294 | ␉{␊ |
295 | ␉␉AuthorizationRef auth = [[authView authorization] authorizationRef];␊ |
296 | ␉␉executePrivilegedCmd(auth,"/usr/bin/osascript","-e 'tell app \"System Events\" to restart'");␊ |
297 | ␉}␊ |
298 | ␉␊ |
299 | }␊ |
300 | - (IBAction)onShutdown: (id)sender␊ |
301 | {␊ |
302 | ␉NSInteger n = NSRunAlertPanel(@"Shutting Down OS X", ␊ |
303 | ␉␉␉␉␉␉␉␉ @"Are you sure you want to shut down your computer now ?",␊ |
304 | ␉␉␉␉␉␉␉␉ @"OK", @"Cancel", /*ThirdButtonHere:*/nil␊ |
305 | ␉␉␉␉␉␉␉␉ /*, args for a printf-style msg go here */);␊ |
306 | ␉if (n==1)␊ |
307 | ␉{␊ |
308 | ␉␉system("/usr/bin/osascript -e 'tell app \"System Events\" to shut down'");␊ |
309 | ␉}␊ |
310 | ␉␊ |
311 | }␊ |
312 | ␊ |
313 | - (IBAction)onSleep: (id)sender␊ |
314 | {␊ |
315 | ␉system("/usr/bin/osascript -e 'tell app \"System Events\" to sleep'");␊ |
316 | }␊ |
317 | ␊ |
318 | //--------------------------------------------------------------------------␊ |
319 | - (void)tableViewSelectionDidChange:(NSNotification *)notification {␊ |
320 | ␊ |
321 | ␉NSTableView* tv= mPartitionsTable;␊ |
322 | ␉if ([tv selectedRow] != currentRowSel)␊ |
323 | ␉{␊ |
324 | ␉␉currentRowSel = [tv selectedRow];␊ |
325 | ␉␉if (currentRowSel>=0)␊ |
326 | ␉␉{␊ |
327 | ␉␉␉const std::vector<PartitionInfo>& partInfo = partExtractor->partList();␊ |
328 | ␉␉␉char hd[7+1]="hd(n,m)";␊ |
329 | ␉␉␉hd[3]= ('0'+partInfo[currentRowSel].disk());␊ |
330 | ␉␉␉hd[5]= ('0'+partInfo[currentRowSel].partition());␉␊ |
331 | ␉␉␉AuthorizationRef auth= [self isUnlocked] ? [[authView authorization] authorizationRef] : NULL;␊ |
332 | ␉␉␉if(prop->setStringForKey(kDefaultPartition, hd))␊ |
333 | ␉␉␉ prop->save(auth);␊ |
334 | ␉␉}␊ |
335 | ␉}␊ |
336 | ␉␉␊ |
337 | }␊ |
338 | ␊ |
339 | //--------------------------------------------------------------------------␊ |
340 | - (NSInteger) numberOfRowsInTableView:(NSTableView *)tableView␊ |
341 | {␊ |
342 | ␉return partExtractor ? partExtractor->partList().size() : 0;␊ |
343 | }␊ |
344 | ␊ |
345 | //--------------------------------------------------------------------------␊ |
346 | - (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row␊ |
347 | {␊ |
348 | ␉id ret=nil;␊ |
349 | ␉␊ |
350 | ␉const std::vector<PartitionInfo>& partInfo = partExtractor->partList();␊ |
351 | ␊ |
352 | ␉if (tableColumn == mPartitionImgColumn) ␊ |
353 | ␉{␊ |
354 | ␉␉switch(partInfo[row].imageIndexFromFs())␊ |
355 | ␉␉{␊ |
356 | ␉␉␉case 0:␊ |
357 | ␉␉␉␉ret = mMacOSXImage;␊ |
358 | ␉␉␉␉break;␊ |
359 | ␉␉␉case 1:␊ |
360 | ␉␉␉␉ret = mWindowsImage;␊ |
361 | ␉␉␉␉break;␊ |
362 | ␉␉␉case 2:␊ |
363 | ␉␉␉␉ret = mLinuxImage;␊ |
364 | ␉␉␉␉break;␊ |
365 | ␉␉␉defualt:␊ |
366 | ␉␉␉␉ret = mUnknownImage;␊ |
367 | ␉␉␉␉break;␊ |
368 | ␉␉␉␉␊ |
369 | ␉␉}␊ |
370 | ␉}␊ |
371 | ␉if (tableColumn == mFileSystemColumn) ␊ |
372 | ␉{␊ |
373 | ␉␉ret = [NSString stringWithFormat: @"%s", partInfo[row].fsType().c_str() ];␊ |
374 | ␉}␊ |
375 | ␉else if (tableColumn == mPartitionNameColumn) ␊ |
376 | ␉{␊ |
377 | ␉␉ret = [NSString stringWithFormat: @"%s", ␊ |
378 | ␉␉␉␉partInfo[row].label().c_str()␊ |
379 | ␉␉␉␉];␊ |
380 | ␉}␊ |
381 | ␉else if (tableColumn == mPartitionIDColumn) ␊ |
382 | ␉{␊ |
383 | ␉␉ret= [NSString stringWithFormat: @"hd(%d,%d)", ␊ |
384 | ␉␉␉␉partInfo[row].disk(),␊ |
385 | ␉␉␉␉partInfo[row].partition()␊ |
386 | ␉␉␉␉];␊ |
387 | ␉} ␊ |
388 | ␉␊ |
389 | ␉return ret;␊ |
390 | }␊ |
391 | //--------------------------------------------------------------------------␊ |
392 | ␊ |
393 | @end␊ |
394 | |