OSX Preference Pane Source Import
[openafs.git] / src / platform / DARWIN / AFSPreference / AFSMenuExtra.m
1 //
2 //  AFSMenuExtra.m
3 //  AFSCommander
4 //
5 //  Created by Claudio on 10/07/07.
6 //  Copyright 2007 INFN - National Institute of Nuclear Physics. All rights reserved.
7 //
8
9 #import "AFSMenuExtra.h"
10 #import "AFSMenuExtraView.h"
11 #import "AFSPropertyManager.h"
12 #import "TaskUtil.h"
13 #import "TokenCredentialController.h"
14 #include <sys/param.h>
15 #include <sys/stat.h>
16 #include <sys/wait.h>
17 #include <sys/types.h>
18 #include <sys/fcntl.h>
19 #include <sys/errno.h>
20 #include <unistd.h>
21 #include <stdlib.h>
22 #include <string.h>
23
24 @implementation AFSMenuExtra
25 // -------------------------------------------------------------------------------
26 //  -(id) initWithBundle
27 // -------------------------------------------------------------------------------
28 - (id) initWithBundle:(NSBundle *)bundle
29 {
30     self = [super initWithBundle:bundle];
31     if( self == nil )
32         return nil;
33         
34         // allocate the lock
35         tokensLock = [[NSLock alloc] init];
36         // inizialize the afspath
37         afsSysPath = nil;
38         credentialMenuController = nil;
39
40     theView = [[AFSMenuExtraView alloc] initWithFrame:[[self view] frame] 
41                                                                                         menuExtra:self];
42         [self setView:theView];
43     
44         
45     // Get the imge for menu
46         //Load image for menu rappresentation
47         hasTokenImage = [self getImageFromBundle:@"hasToken" 
48                                                                                          fileExt:@"png"];
49         
50         noTokenImage = [self getImageFromBundle:@"noToken" 
51                                                                                         fileExt:@"png"];
52         
53     theMenu = [[NSMenu alloc] initWithTitle: @""];
54     [theMenu setAutoenablesItems: NO];
55     startStopMenu = [theMenu addItemWithTitle: kAfsOff action: @selector(startStopAfs:)  keyEquivalent: @""];
56         [startStopMenu setTarget:self];
57         
58         loginMenu = [theMenu addItemWithTitle: kMenuLogin action: @selector(getToken:)  keyEquivalent: @""];
59         [loginMenu setTarget:self];
60
61         unlogMenu = [theMenu addItemWithTitle: kMenuUnlog action: @selector(releaseToken:)  keyEquivalent: @""];
62         [unlogMenu setTarget:self];
63
64         // Register for preference user change
65         [[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(readPreferenceFile:) 
66                                                                                                                         name:kAFSMenuExtraID object:kPrefChangeNotification];
67         
68         // Register for afs state change
69         [[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(afsVolumeMountChange:) 
70                                                                                                                         name:kAFSMenuExtraID object:kMExtraAFSStateChange];
71         
72         
73         //Register for mount/unmount afs volume
74         [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self 
75                                                                                                                    selector:@selector(afsVolumeMountChange:) 
76                                                                                                                            name:NSWorkspaceDidMountNotification object:nil];
77         
78         [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self 
79                                                                                                                    selector:@selector(afsVolumeMountChange:) 
80                                                                                                                            name:NSWorkspaceDidUnmountNotification object:nil];
81         //Start to read the afs path
82         [self readPreferenceFile:nil];  
83         [self startTimer];
84     return self;
85 }
86
87 // -------------------------------------------------------------------------------
88 //  -(void) willUnload
89 // -------------------------------------------------------------------------------
90 - (void) willUnload {
91         //release the lock
92         [self stopTimer];
93         
94         if(hasTokenImage) [hasTokenImage release];
95         if(noTokenImage) [noTokenImage release];
96         
97         // Unregister for preference change
98         [[NSDistributedNotificationCenter defaultCenter] removeObserver:self name:kAFSMenuExtraID object:kPrefChangeNotification];
99         [[NSDistributedNotificationCenter defaultCenter] removeObserver:self name:kAFSMenuExtraID object:kMExtraAFSStateChange];
100         [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:self name:NSWorkspaceDidMountNotification object:nil];
101         [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:self name:NSWorkspaceDidUnmountNotification object:nil];
102
103         
104         [tokensLock release];
105 }
106
107 // -------------------------------------------------------------------------------
108 //  startTimer:
109 // -------------------------------------------------------------------------------
110 - (void)startTimer{
111         //start the time for check tokens validity
112         if(timerForCheckTokensList) return;
113         timerForCheckTokensList = [NSTimer scheduledTimerWithTimeInterval:TOKENS_REFRESH_TIME_IN_SEC 
114                                                                                                                            target:self 
115                                                                                                                          selector:@selector(updateAfsStatus:) 
116                                                                                                                          userInfo:nil 
117                                                                                                                           repeats:YES];
118         [timerForCheckTokensList fire]; 
119 }
120
121 // -------------------------------------------------------------------------------
122 //  stopTimer:
123 // -------------------------------------------------------------------------------
124 - (void)stopTimer{
125         if(!timerForCheckTokensList) return;
126         [timerForCheckTokensList invalidate];   
127         timerForCheckTokensList = nil;
128 }
129 // -------------------------------------------------------------------------------
130 //  -(void) dealloc
131 // -------------------------------------------------------------------------------
132 - (void) dealloc
133 {
134     [theMenu release];
135     [theView release];
136         if(afsSysPath) [afsSysPath release];
137         // send notify that menuextra has closed
138         [[NSDistributedNotificationCenter defaultCenter] postNotificationName:afsCommanderID object:kPrefChangeNotification];
139     [super dealloc];
140 }
141
142 // -------------------------------------------------------------------------------
143 //  -(NSMenu*) menu
144 // -------------------------------------------------------------------------------
145 - (NSMenu *) menu
146 {
147     return theMenu;
148 }
149
150
151 // -------------------------------------------------------------------------------
152 //  -(void) readPreferenceFile
153 // -------------------------------------------------------------------------------
154 - (void) readPreferenceFile:(NSNotification *)notification
155 {
156         NSLog(@"Reading preference file");
157         //CFPreferencesSynchronize((CFStringRef)afsCommanderID,  kCFPreferencesAnyUser, kCFPreferencesAnyHost);
158         //CFPreferencesSynchronize((CFStringRef)afsCommanderID,  kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
159         
160         if(afsSysPath) {
161                 [afsSysPath release];
162                 afsSysPath = nil;
163         }
164         
165         afsSysPath = PREFERENCE_AFS_SYS_PAT_STATIC;/*(NSString*)CFPreferencesCopyValue((CFStringRef)PREFERENCE_AFS_SYS_PAT, 
166                                                                                                    (CFStringRef)afsCommanderID,  
167                                                                                                    kCFPreferencesAnyUser, 
168                                                                                                    kCFPreferencesAnyHost);*/
169         //NSLog(@"Path readed %s", (afsSysPath==nil?[afsSysPath UTF8String]:@" no path found "));
170                 
171         // read the preference for aklog use
172         useAklogPrefValue = (NSNumber*)CFPreferencesCopyValue((CFStringRef)PREFERENCE_USE_AKLOG, 
173                                                                                                                   (CFStringRef)afsCommanderID, 
174                                                                                                                   kCFPreferencesCurrentUser, 
175                                                                                                                   kCFPreferencesAnyHost);
176         //set the menu name
177         //NSLog(@"Preference readed for useAklogPrefValue %d", [useAklogPrefValue intValue]);
178         
179         [self updateAfsStatus:nil];
180 }
181
182 // -------------------------------------------------------------------------------
183 //  -(void) readPreferenceFile
184 // -------------------------------------------------------------------------------
185 - (void)startStopAfs:(id)sender
186 {
187         NSLog(@"startStopAfs: %s",[afsSysPath UTF8String]);
188         if(!afsSysPath) return;
189         
190         OSStatus status = noErr;
191         NSString *afsdPath = [TaskUtil searchExecutablePath:@"afsd"];
192         NSString *rootHelperApp = nil;
193         BOOL currentAfsState = NO;
194         
195         @try {
196                 if(afsdPath == nil) return;
197                 AFSPropertyManager *afsMngr = [[AFSPropertyManager alloc] initWithAfsPath:afsSysPath];
198                 currentAfsState = [afsMngr checkAfsStatus];
199                 [afsMngr release];
200                 
201                 rootHelperApp = [[self bundle] pathForResource:@"afshlp" ofType:@""];
202                 
203                 //[startStopScript setString: resourcePath];
204                 //NSLog(@"LAunch repair HelperTool");
205                 //Check helper app
206                 [self repairHelperTool];
207                 
208                 // make the parameter to call the root helper app
209                 //NSLog(@"Path installazione afs: %s",[afsSysPath UTF8String]);
210                 //NSLog(@"Path installazione afsd: %s", [afsdPath UTF8String]);
211                 status = [[AuthUtil shared] autorize];
212                 if(status == noErr){
213                         if(currentAfsState){
214                                 //shutdown afs
215                                 //NSLog(@"Shutting down afs");
216                                 NSMutableString *afsKextPath = [[NSMutableString alloc] initWithCapacity:256];
217                                 [afsKextPath setString:afsSysPath];
218                                 [afsKextPath appendString:@"/etc/afs.kext"];
219                                 
220                                 //NSLog(@"executeTaskWithAuth");
221                                 const char *stopAfsArgs[] = {"stop_afs", [afsKextPath  UTF8String], [afsdPath UTF8String], 0L};
222                                 [[AuthUtil shared] execUnixCommand:[rootHelperApp UTF8String] 
223                                                                                           args:stopAfsArgs
224                                                                                         output:nil];
225                         } else {
226                                 //NSLog(@"Starting up afs");
227                                 const char *startAfsArgs[] = {[[[self bundle] pathForResource:@"start_afs" ofType:@"sh"]  UTF8String], [afsSysPath UTF8String], [afsdPath UTF8String], 0L};
228                                 [[AuthUtil shared] execUnixCommand:[rootHelperApp UTF8String] 
229                                                                                           args:startAfsArgs
230                                                                                         output:nil];
231                         }
232                 }
233         }
234         @catch (NSException * e) {
235                 NSLog([e reason]);
236         }
237         @finally {
238                 [[AuthUtil shared] deautorize];
239                 [self updateAfsStatus:nil];
240                 //Send notification to preferencepane
241                 [[NSDistributedNotificationCenter defaultCenter] postNotificationName:afsCommanderID object:kMenuExtraEventOccured];
242         }
243         
244 }
245
246 // -------------------------------------------------------------------------------
247 //  -(void) getToken
248 // -------------------------------------------------------------------------------
249 - (void)getToken:(id)sender
250 {
251         
252         NSRect globalRect;
253         globalRect.origin = [[[self view] window] convertBaseToScreen:[[self view] frame].origin];
254         globalRect.size = [[self view] frame].size;
255         AFSPropertyManager *afsPropMngr = [[AFSPropertyManager alloc] initWithAfsPath:afsSysPath ];
256         [afsPropMngr loadConfiguration]; 
257         
258         
259         if([useAklogPrefValue intValue]==NSOnState ) {
260                 NSLog(@"Use Aklog");
261                 [afsPropMngr getTokens:false 
262                                                    usr:nil 
263                                                    pwd:nil];
264                 [self klogUserEven:nil];
265         } else {
266                 NSLog(@"Use Klog");
267                 // register for user event
268                 [[NSDistributedNotificationCenter defaultCenter] addObserver:self 
269                                                                                                                         selector:@selector(klogUserEven:) 
270                                                                                                                                 name:kAFSMenuExtraID 
271                                                                                                                           object:kLogWindowClosed];
272         
273                 credentialMenuController = [[AFSMenuCredentialContoller alloc] initWhitRec:globalRect 
274                                                                                                                                         afsPropManager:afsPropMngr];
275                 [credentialMenuController showWindow];
276         }
277         
278         //Dispose afs manager
279         [afsPropMngr release];
280 }
281
282 // -------------------------------------------------------------------------------
283 //  -(void) releaseToken
284 // -------------------------------------------------------------------------------
285 - (void)releaseToken:(id)sender
286 {
287         AFSPropertyManager *afsMngr = [[AFSPropertyManager alloc] initWithAfsPath:afsSysPath];
288         [afsMngr unlog:nil];
289         [afsMngr release];
290         [self updateAfsStatus:nil];
291 }
292
293
294 // -------------------------------------------------------------------------------
295 //  -(void) afsVolumeMountChange - Track for mount unmount afs volume
296 // -------------------------------------------------------------------------------
297 - (void) afsVolumeMountChange:(NSNotification *)notification{
298         [self updateAfsStatus:nil];
299 }
300
301 // -------------------------------------------------------------------------------
302 //  -(void) updateAfsStatus
303 // -------------------------------------------------------------------------------
304 - (void)updateAfsStatus:(NSTimer*)timer
305 {
306         //Try to locking
307         if(![tokensLock tryLock]) return;
308         
309         // check the afs state in esclusive mode
310         AFSPropertyManager *afsMngr = [[AFSPropertyManager alloc] initWithAfsPath:afsSysPath];
311         afsState = [afsMngr checkAfsStatus];
312         
313         NSArray *tokens = [afsMngr getTokenList];
314         [afsMngr release];
315         gotToken = [tokens count] > 0;
316         [tokens release];
317         // update the menu item title
318         [startStopMenu setTitle:afsState?kAfsButtonShutdown:kAfsButtonStartup];
319         
320         [self updateMenu];
321         
322         [theView setNeedsDisplay:YES];
323         
324         //unlock
325         [tokensLock unlock];
326 }
327
328 // -------------------------------------------------------------------------------
329 //  -(void) klogUserEven
330 // -------------------------------------------------------------------------------
331 -(void) klogUserEven:(NSNotification *)notification
332 {
333         if(credentialMenuController) {
334                 [[NSDistributedNotificationCenter defaultCenter] removeObserver:self name:kAFSMenuExtraID object:kLogWindowClosed];
335                 [credentialMenuController closeWindow];
336                 [credentialMenuController release];
337                 credentialMenuController = nil;
338         }
339         //Send notification to PreferencePane
340         [[NSDistributedNotificationCenter defaultCenter] postNotificationName:afsCommanderID object:kMenuExtraEventOccured];
341         
342         [self updateAfsStatus:nil];
343 }
344
345 // -------------------------------------------------------------------------------
346 //  -(void) getImageFromBundle
347 // -------------------------------------------------------------------------------
348 - (NSImage*)getImageFromBundle:(NSString*)fileName fileExt:(NSString*)ext
349 {
350         return [[NSImage alloc]initWithContentsOfFile:[[self bundle] pathForResource:fileName 
351                                                                                                                                    ofType:ext]];
352 }
353
354 // -------------------------------------------------------------------------------
355 //  -(void) imageToRender
356 // -------------------------------------------------------------------------------
357 - (NSImage*)imageToRender
358 {
359         if(gotToken){
360                 return hasTokenImage;
361         } else {
362                 return noTokenImage;
363         }
364 }
365
366
367 // -------------------------------------------------------------------------------
368 //  -(void) updateMenu
369 // -------------------------------------------------------------------------------
370 - (void)updateMenu{
371         [loginMenu setEnabled:afsState];
372         [unlogMenu setEnabled:afsState];
373 }
374
375
376 // -------------------------------------------------------------------------------
377 //  -(void) useAklogPrefValue
378 // -------------------------------------------------------------------------------
379 - (BOOL)useAklogPrefValue
380 {
381         if(useAklogPrefValue) return [useAklogPrefValue intValue] == NSOnState; 
382         else return NSOffState;
383 }
384
385 // -------------------------------------------------------------------------------
386 //  repairHelperTool:
387 // -------------------------------------------------------------------------------
388 - (void) repairHelperTool
389 {
390         struct stat st;
391     int fdTool;
392         int status = 0;
393         NSLog(@"repairHelperTool"); 
394         NSString *afshlpPath = [[self bundle] pathForResource:@"afshlp" ofType:nil];
395         
396         
397     
398     // Open tool exclusively, so nobody can change it while we bless it.
399     fdTool = open([afshlpPath UTF8String], O_NONBLOCK | O_RDONLY | O_EXLOCK, 0);
400     
401     if(fdTool == -1)
402     {
403         NSLog(@"Exclusive open while repairing tool failed: %d.", errno);
404         exit(-1);
405     }
406     
407     if(fstat(fdTool, &st))
408     {
409         NSLog(@"fstat failed.");
410         exit(-1);
411     }
412     
413     if(st.st_uid != 0)
414     {
415                 status = [[AuthUtil shared] autorize];
416                 if(status == noErr){
417                         fchown(fdTool, 0, st.st_gid);
418                         
419                         // Disable group and world writability and make setuid root.
420                         fchmod(fdTool, (st.st_mode & (~(S_IWGRP | S_IWOTH)))/* | S_ISUID*/);
421                         const char *args[] = {"root", [afshlpPath UTF8String],0L};
422                         [[AuthUtil shared] execUnixCommand:"/usr/sbin/chown" 
423                                                                                   args:args
424                                                                                 output:nil];
425                         [[AuthUtil shared] deautorize];
426                 }
427     } else  NSLog(@"st_uid = 0");
428     
429         
430     
431     close(fdTool);
432     
433     NSLog(@"Self-repair done.");
434         
435 }@end