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