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 | #import "TableViewsController.h"␊ |
13 | #import "PartitionInfoManager.h"␊ |
14 | #import "ShellProcess.h"␊ |
15 | #import "ChameleonPropertyList.h"␊ |
16 | #include <string>␊ |
17 | ␊ |
18 | //--------------------------------------------------------------------------␊ |
19 | // Constants␊ |
20 | //--------------------------------------------------------------------------␊ |
21 | static const char * const szBootPaths[]= {␊ |
22 | ␉"/",␊ |
23 | ␉"/Extra/",␊ |
24 | ␉"/Volumes/EFI/Extra/",␊ |
25 | ␉"/Volumes/Cham/Extra/",␊ |
26 | ␉"/Volumes/BootLoaders/Extra/",␊ |
27 | ␉"/Volumes/RX0/Extra/",␊ |
28 | ␉"/Library/Preferences/SystemConfiguration/",␊ |
29 | ␉NULL␊ |
30 | };␊ |
31 | ␊ |
32 | static const char* const szPropFileName = "org.chameleon.Boot.plist";␊ |
33 | static const int CurrentPreferencesFileVersion = 0x02; // for future back compatibility␊ |
34 | ␊ |
35 | // TODO move table views handling code to a dedicated controller␊ |
36 | ␊ |
37 | //--------------------------------------------------------------------------␊ |
38 | //--------------------------------------------------------------------------␊ |
39 | // Static file variables␊ |
40 | //--------------------------------------------------------------------------␊ |
41 | static std::string sCurrentDefaultPartition;␊ |
42 | ␊ |
43 | static ChameleonPrefPane * _prefPaneInstance = NULL;␊ |
44 | ␊ |
45 | ␊ |
46 | //--------------------------------------------------------------------------␊ |
47 | ␊ |
48 | @implementation ChameleonPrefPane␊ |
49 | ␊ |
50 | + (ChameleonPrefPane *)instance { return(_prefPaneInstance);}␊ |
51 | ␊ |
52 | //--------------------------------------------------------------------------␊ |
53 | - (id) init␊ |
54 | {␊ |
55 | ␉self = [super init];␊ |
56 | ␉_prefPaneInstance = self;␊ |
57 | ␉␊ |
58 | ␉␊ |
59 | ␉␊ |
60 | ␉␊ |
61 | ␉// Retrieve the org.chameleon.prefPane.plist config␊ |
62 | ␉return self;␊ |
63 | }␊ |
64 | - (void)dealloc␊ |
65 | {␊ |
66 | ␉// release the colors␊ |
67 | ␉[mOptionsDict release];␊ |
68 | ␉[mPartitionsDict release];␊ |
69 | ␉␊ |
70 | ␉[super dealloc];␊ |
71 | }␊ |
72 | ␊ |
73 | - (NSMutableDictionary*) preferencesFile {␉return mOptionsDict; }␊ |
74 | - (NSMutableDictionary*) preferencesParts {␉return mPartitionsDict; }␊ |
75 | ␊ |
76 | //--------------------------------------------------------------------------␊ |
77 | -(bool) savePreferences: (NSDictionary*) dict␊ |
78 | {␊ |
79 | ␉␊ |
80 | ␉if(dict==nil) return false;␊ |
81 | ␉␊ |
82 | ␉AuthorizationRef auth = [self isUnlocked] ? [[authView authorization] authorizationRef] : NULL;␊ |
83 | ␉if (!auth) return false;␊ |
84 | ␉␊ |
85 | ␉NSString* tmpPropName = @"/tmp/chamPrefPane.plist";␊ |
86 | ␉BOOL ret = [dict writeToFile:tmpPropName atomically:YES];␊ |
87 | ␉if (!ret) return false;␊ |
88 | ␉NSString* args = [NSString stringWithFormat:@"%@ %@", tmpPropName, kPreferencesFilePath];␊ |
89 | ␉const char * cArgs = [args UTF8String];␊ |
90 | ␉ret = executePrivilegedCmd(auth, "/bin/cp", cArgs , NULL);␊ |
91 | ␉return ret;␊ |
92 | }␊ |
93 | ␊ |
94 | //--------------------------------------------------------------------------␊ |
95 | // SFAuthorization implementation␊ |
96 | //--------------------------------------------------------------------------␊ |
97 | ␊ |
98 | // SFAuthorization delegates␊ |
99 | - (void)authorizationViewDidAuthorize:(SFAuthorizationView *)view {␊ |
100 | [self selectDefaultPartition];␊ |
101 | ␉[self refreshLockStates];␊ |
102 | ␊ |
103 | } ␊ |
104 | ␊ |
105 | //--------------------------------------------------------------------------␊ |
106 | - (void)authorizationViewDidDeauthorize:(SFAuthorizationView *)view {␊ |
107 | [self refreshLockStates];␊ |
108 | }␊ |
109 | ␊ |
110 | //--------------------------------------------------------------------------␊ |
111 | // Setup security for changing boot options␊ |
112 | -(void) initAuthorization␊ |
113 | {␊ |
114 | AuthorizationItem items = {kAuthorizationRightExecute, 0, NULL, 0};␊ |
115 | AuthorizationRights rights = {1, &items};␊ |
116 | ␊ |
117 | ␉[authView setAuthorizationRights:&rights];␊ |
118 | authView.delegate = self;␊ |
119 | [authView updateStatus:nil];␊ |
120 | }␊ |
121 | //--------------------------------------------------------------------------␊ |
122 | - (␉AuthorizationRef) auth␊ |
123 | {␊ |
124 | ␉return [self isUnlocked] ? [[authView authorization] authorizationRef] : NULL;␊ |
125 | ␉␊ |
126 | }␊ |
127 | ␊ |
128 | //--------------------------------------------------------------------------␊ |
129 | - (void) refreshLockStates␊ |
130 | {␊ |
131 | [[TableViewsController instance]->mPartitionsTable setEnabled:[self isUnlocked]];␊ |
132 | [mStatusText setEnabled:[self isUnlocked]];␊ |
133 | ␉␊ |
134 | ␉// Refresh other panels␊ |
135 | ␉[PreferencesControllerBase doForEachGroup: RefreshLockStates withOption: nil];␊ |
136 | }␊ |
137 | ␊ |
138 | ␊ |
139 | //--------------------------------------------------------------------------␊ |
140 | - (bool)isUnlocked ␊ |
141 | {␊ |
142 | return [authView authorizationState] == SFAuthorizationViewUnlockedState;␊ |
143 | }␊ |
144 | ␊ |
145 | //--------------------------------------------------------------------------␊ |
146 | /** When called here, all outlets references are initialized */␊ |
147 | - (void)awakeFromNib␊ |
148 | { // called more than once, we only need one resource init␊ |
149 | ␉static bool ft=true;␊ |
150 | ␉if(ft)␊ |
151 | ␉{␊ |
152 | ␉␉ft=false;␊ |
153 | ␉␉[self␉loadPreferences];␊ |
154 | ␉␉[self initBootConfig];␊ |
155 | ␉}␊ |
156 | }␊ |
157 | ␊ |
158 | //--------------------------------------------------------------------------␊ |
159 | - (void) loadPreferences␊ |
160 | {␊ |
161 | ␉// test with preferences file already created and when no prefs exists␊ |
162 | ␉␊ |
163 | ␉id oldGlobalPreferences = [ [NSDictionary dictionaryWithContentsOfFile: ␊ |
164 | ␉␉␉␉␉␉␉␉ kPreferencesFilePath ] retain];␊ |
165 | ␉␊ |
166 | ␉mPartitionsDict = [[NSMutableDictionary alloc] init];␊ |
167 | ␉[mPartitionsDict retain];␊ |
168 | ␉␊ |
169 | ␉// Initialize bootConfig desc dict␊ |
170 | ␉[PreferencesControllerBase doForEachGroup: AddOptionsDesc withOption: nil];␊ |
171 | ␉␊ |
172 | ␉if (oldGlobalPreferences!=nil)␊ |
173 | ␉{␊ |
174 | ␉␉mPreferenceFileVersion= [[oldGlobalPreferences objectForKey: keyPreferencesFileVersion] intValue ];␊ |
175 | ␉␉[PreferencesControllerBase doForEachGroup: LoadPreferencesOptions withOption: oldGlobalPreferences];␊ |
176 | ␉␉[mPartitionsDict addEntriesFromDictionary: [oldGlobalPreferences objectForKey: keyPartitionsList] ];␊ |
177 | ␉}␊ |
178 | ␉else ␊ |
179 | ␉{ // Create a preference plist file with Defaults values␊ |
180 | ␉␉oldGlobalPreferences = [[NSMutableDictionary alloc] init];␊ |
181 | ␉␉[PreferencesControllerBase doForEachGroup: SetDefaultValues withOption: oldGlobalPreferences];␊ |
182 | ␉␉␊ |
183 | ␉␉// Initialize defaults␊ |
184 | ␉␉[oldGlobalPreferences setObject: [[NSNumber alloc] initWithInt: CurrentPreferencesFileVersion] ␊ |
185 | ␉␉␉␉␉␉␉␉ forKey: keyPreferencesFileVersion];␊ |
186 | ␉␉[oldGlobalPreferences setObject: mPartitionsDict forKey: keyPartitionsList];␊ |
187 | ␉␉␊ |
188 | ␉␉// Save the preferences file␊ |
189 | ␉␉[ self savePreferences:oldGlobalPreferences ];␊ |
190 | ␉}␊ |
191 | ␉␊ |
192 | ␉mOptionsDict = [[NSMutableDictionary alloc] init];␊ |
193 | ␉[mOptionsDict addEntriesFromDictionary:oldGlobalPreferences];␊ |
194 | ␉if (mPartitionsDict!=nil) [mPartitionsDict retain];␊ |
195 | ␉[oldGlobalPreferences release];␊ |
196 | }␊ |
197 | ␊ |
198 | //--------------------------------------------------------------------------␊ |
199 | - (void) initBootConfig␊ |
200 | {␊ |
201 | ␉static bool ft=true;␊ |
202 | ␊ |
203 | ␉[self initAuthorization];␊ |
204 | ␉␊ |
205 | ␉if (!BootProp::instance().isValid())␊ |
206 | ␉{␊ |
207 | ␉␉std::string sPath;␊ |
208 | ␉␉CFStringRef errorString=NULL;␊ |
209 | ␉␉bool cont =true;␊ |
210 | ␉␉␊ |
211 | ␉␉id sForcedPath = [mOptionsDict valueForKey: keyForceBootConfigPath];␊ |
212 | ␉␉const char * szForcedPath = sForcedPath!=nil ? [sForcedPath UTF8String] : NULL;␊ |
213 | ␉␉if (szForcedPath && *szForcedPath)␊ |
214 | ␉␉{␊ |
215 | ␉␉␉cont = !BootProp::instance().open(szForcedPath, false, [self auth]);␊ |
216 | ␉␉}␊ |
217 | ␉␉else {␊ |
218 | ␉␉␉for(int i=0; szBootPaths[i] && cont; i++)␊ |
219 | ␉␉␉{␊ |
220 | ␉␉␉␉sPath = szBootPaths[i];␊ |
221 | ␉␉␉␉sPath += szPropFileName;␊ |
222 | ␉␉␉␉cont = !BootProp::instance().open(sPath.c_str(), false, [self auth]);␊ |
223 | ␉␉␉}␊ |
224 | ␉␉}␊ |
225 | ␉␉if (cont)␊ |
226 | ␉␉{␊ |
227 | ␉␉␉if(!ft) return;␊ |
228 | ␉␉␉ft=false;␊ |
229 | ␉␉␉[mStatusText setTextColor: [NSColor redColor] ];␊ |
230 | ␉␉␉if (errorString)␊ |
231 | ␉␉␉␉[mStatusText setStringValue:[NSString stringWithFormat: ␊ |
232 | ␉␉␉␉␉␉␉␉␉␉␉ GetLocStrDef(@"Error_Parsing", @"error", @"Error while parsing %@"),␊ |
233 | ␉␉␉␉␉␉␉␉␉␉␉ errorString] ];␊ |
234 | ␉␉␉else␊ |
235 | ␉␉␉␉[mStatusText setStringValue: ␊ |
236 | ␉␉␉␉GetLocStrDef(@"Error_Searching", @"Error while searching for org.chameleon.Boot.plist", @"error") ];␊ |
237 | ␉␉␉␊ |
238 | ␉␉}␊ |
239 | ␉␉else␊ |
240 | ␉␉{ ␊ |
241 | ␉␉␉[mStatusText setTextColor: [NSColor grayColor] ];␊ |
242 | ␉␉␉NSString* ns = [ [NSString alloc] initWithUTF8String:BootProp::instance().propFilePath() ];␊ |
243 | ␉␉␉[mStatusText setStringValue: [NSString stringWithFormat: @"bootConfig: %@", ns] ];␊ |
244 | ␉␉␉BootSetupController* bsc = [BootSetupController instance];␊ |
245 | ␉␉␉[bsc->mBootConfigPathText setStringValue: [[NSString alloc] initWithUTF8String: BootProp::instance().propFilePath()] ];␊ |
246 | ␉␉␉[bsc->mBootExtraPathText setStringValue: [[bsc->mBootConfigPathText stringValue] stringByDeletingLastPathComponent] ];␊ |
247 | ␉␉␉[bsc->mBootCdbootPathText setStringValue: [[bsc->mBootConfigPathText stringValue] stringByDeletingLastPathComponent]];␊ |
248 | ␊ |
249 | ␉␉}␊ |
250 | ␉␉␊ |
251 | ␉␉[PartsInfoMgr resetSwapping];␊ |
252 | ␊ |
253 | ␉␉if (BootProp::instance().isValid())␊ |
254 | ␉␉{␊ |
255 | ␉␉␉// read options in the plist file␊ |
256 | ␉␉␉const char *s = BootProp::instance().getStringForKey(kHidePartition) ;␊ |
257 | ␉␉␉if (s==nil) s = "";␊ |
258 | ␉␉␉[PartsInfoMgr hideParts: [NSString stringWithUTF8String: s] ];␊ |
259 | ␊ |
260 | ␉␉␉s = BootProp::instance().getStringForKey(kRenamePartition);␊ |
261 | ␉␉␉if (s==nil) s = "";␊ |
262 | ␉␉␉[PartsInfoMgr renameParts: [NSString stringWithUTF8String: s]];␊ |
263 | ␉␉␉␊ |
264 | ␊ |
265 | ␉␉␉id val = [mOptionsDict valueForKey: keySwapHD01];␊ |
266 | ␉␉␉[[BootSetupController instance]->mSwapHD01 setIntValue: [val intValue] ];␊ |
267 | ␉␉␉[[BootSetupController instance] doSwapHD: [val boolValue] save: false src:0 dst:1];␊ |
268 | ␉␉␉␊ |
269 | ␉␉␉val = [mOptionsDict valueForKey: keySwapHD02];␊ |
270 | ␉␉␉[[BootSetupController instance]->mSwapHD02 setIntValue: [val intValue] ];␊ |
271 | ␉␉␉[[BootSetupController instance] doSwapHD: [val boolValue] save: false src:0 dst:2];␊ |
272 | ␊ |
273 | ␉␉␉// Load all boot Options into the interface components␊ |
274 | ␉␉␉[PreferencesControllerBase loadOptionsFromBootFile];␊ |
275 | ␉␉␉[self selectDefaultPartition];␊ |
276 | ␉␉␉␉ ␊ |
277 | ␉␉}␊ |
278 | ␉␉␊ |
279 | ␉}␉␊ |
280 | }␊ |
281 | //--------------------------------------------------------------------------␊ |
282 | - (void) selectDefaultPartition␊ |
283 | {␊ |
284 | if(!authView) return;␊ |
285 | ␉␊ |
286 | ␉[self refreshLockStates];␊ |
287 | ␉␊ |
288 | ␉// try to get the current default partition if any␊ |
289 | ␉if(BootProp::instance().isValid())␊ |
290 | ␉{␊ |
291 | ␉␉const char *sdp = BootProp::instance().getStringForKey(kDefaultPartition);␊ |
292 | ␉␉sCurrentDefaultPartition = sdp ? sdp : ""; ␊ |
293 | ␉␉if (sCurrentDefaultPartition.size())␊ |
294 | ␉␉{␊ |
295 | ␉␉␉NSUInteger index=0;␊ |
296 | ␉␉␉PartitionInfoElement* p = [PartsInfoMgr partWithName:[NSString stringWithUTF8String:sCurrentDefaultPartition.c_str()] outIndex: &index];␊ |
297 | ␉␉␉if (p!=nil)␊ |
298 | ␉␉␉{␊ |
299 | ␉␉␉␉[[TableViewsController instance ]->mPartitionsTable selectRowIndexes: [NSIndexSet indexSetWithIndex:index] byExtendingSelection:NO];␊ |
300 | ␉␉␉}␊ |
301 | ␉␉}␊ |
302 | ␉}␊ |
303 | ␉␊ |
304 | }␊ |
305 | ␊ |
306 | //--------------------------------------------------------------------------␊ |
307 | // following DieBuch recommendation : using applescript and system events (thanks!):␊ |
308 | - (IBAction)onRestart: (id)sender␊ |
309 | {␊ |
310 | ␉NSInteger n = NSRunAlertPanel(@"Restarting OS X", ␊ |
311 | ␉␉␉␉␉␉␉␉ @"Are you sure you want to restart your computer now ?",␊ |
312 | ␉␉␉␉␉␉␉␉ @"OK", @"Cancel", nil);␊ |
313 | ␉if (n==1)␊ |
314 | ␉{␊ |
315 | ␉␉AuthorizationRef auth = [[authView authorization] authorizationRef];␊ |
316 | ␉␉executePrivilegedCmd(auth,"/usr/bin/osascript","-e 'tell app \"System Events\" to restart'");␊ |
317 | ␉}␊ |
318 | ␉␊ |
319 | }␊ |
320 | //--------------------------------------------------------------------------␊ |
321 | - (IBAction)onShutdown: (id)sender␊ |
322 | {␊ |
323 | ␉NSInteger n = NSRunAlertPanel(@"Shutting Down OS X", ␊ |
324 | ␉␉␉␉␉␉␉␉ @"Are you sure you want to shut down your computer now ?",␊ |
325 | ␉␉␉␉␉␉␉␉ @"OK", @"Cancel", /*ThirdButtonHere:*/nil␊ |
326 | ␉␉␉␉␉␉␉␉ /*, args for a printf-style msg go here */);␊ |
327 | ␉if (n==1)␊ |
328 | ␉{␊ |
329 | ␉␉system("/usr/bin/osascript -e 'tell app \"System Events\" to shut down'");␊ |
330 | ␉}␊ |
331 | }␊ |
332 | ␊ |
333 | - (IBAction)onSleep: (id)sender␊ |
334 | {␊ |
335 | ␉system("/usr/bin/osascript -e 'tell app \"System Events\" to sleep'");␊ |
336 | }␊ |
337 | ␊ |
338 | //--------------------------------------------------------------------------␊ |
339 | ␊ |
340 | @end␊ |
341 | |