5 // Created by Claudio Bisegni on 29/07/09.
6 // Copyright 2009 Infn. All rights reserved.
9 #import "AFSBackgrounderDelegate.h"
10 #import "AFSMenuExtraView.h"
11 #import "AFSPropertyManager.h"
13 #import "TokenCredentialController.h"
14 #include <sys/param.h>
17 #include <sys/types.h>
18 #include <sys/fcntl.h>
19 #include <sys/errno.h>
24 @implementation AFSBackgrounderDelegate
25 #pragma mark NSApp Delegate
26 - (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
27 afsMngr = [[AFSPropertyManager alloc] initWithAfsPath:afsSysPath];
28 // allocate the lock for concurent afs check state
29 tokensLock = [[NSLock alloc] init];
31 //remove the auto eanble on menu item
32 [backgrounderMenu setAutoenablesItems:NO];
33 //Sets the images in our NSStatusItem
37 // Get the imge for menu
38 //Load image for menu rappresentation
39 hasTokenImage = [self getImageFromBundle:@"hasToken"
42 noTokenImage = [self getImageFromBundle:@"noToken"
44 //get the sazi of the menu icon
45 menuSize = [hasTokenImage size];
46 //Start to read the afs path
47 [self readPreferenceFile:nil];
52 // Register for preference user change
53 [[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(readPreferenceFile:)
54 name:kAFSMenuExtraID object:kPrefChangeNotification];
56 // Register for afs state change
57 [[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(afsVolumeMountChange:)
58 name:kAFSMenuExtraID object:kMExtraAFSStateChange];
60 // Register for menu state change
61 [[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(chageMenuVisibility:)
62 name:kAFSMenuExtraID object:kMExtraAFSMenuChangeState];
64 //Register for mount/unmount afs volume
65 [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self
66 selector:@selector(afsVolumeMountChange:)
67 name:NSWorkspaceDidMountNotification object:nil];
69 [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self
70 selector:@selector(afsVolumeMountChange:)
71 name:NSWorkspaceDidUnmountNotification object:nil];
73 [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self
74 selector:@selector(switchHandler:)
75 name:NSWorkspaceSessionDidBecomeActiveNotification
78 [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self
79 selector:@selector(switchHandler:)
80 name:NSWorkspaceSessionDidResignActiveNotification
83 //try to see if we need tho show the menu at startup
85 [self setStatusItem:[showStatusMenu boolValue]];
87 NSLog(@"Check if we need to get token at login time %d", [aklogTokenAtLogin intValue]);
88 if([aklogTokenAtLogin boolValue] && afsState && !gotToken) {
89 NSLog(@"Proceed to get token");
90 //check if we must get the aklog at logint(first run of backgrounder
95 - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender {
96 if(afsSysPath) [afsSysPath release];
101 if(hasTokenImage) [hasTokenImage release];
102 if(noTokenImage) [noTokenImage release];
104 // Unregister for preference change
105 [[NSDistributedNotificationCenter defaultCenter] removeObserver:self name:NSWorkspaceSessionDidBecomeActiveNotification object:nil];
106 [[NSDistributedNotificationCenter defaultCenter] removeObserver:self name:NSWorkspaceSessionDidResignActiveNotification object:nil];
107 [[NSDistributedNotificationCenter defaultCenter] removeObserver:self name:kAFSMenuExtraID object:kPrefChangeNotification];
108 [[NSDistributedNotificationCenter defaultCenter] removeObserver:self name:kAFSMenuExtraID object:kMExtraAFSStateChange];
109 [[NSDistributedNotificationCenter defaultCenter] removeObserver:self name:kAFSMenuExtraID object:kMExtraAFSMenuChangeState];
110 [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:self name:NSWorkspaceDidMountNotification object:nil];
111 [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:self name:NSWorkspaceDidUnmountNotification object:nil];
114 // send notify that menuextra has closed
115 [[NSDistributedNotificationCenter defaultCenter] postNotificationName:kAfsCommanderID object:kPrefChangeNotification];
117 if(tokensLock) [tokensLock release];
118 if(afsMngr) [afsMngr release];
119 return NSTerminateNow;
121 #pragma mark Notification Handler
122 // -------------------------------------------------------------------------------
123 // -(void) readPreferenceFile
124 // -------------------------------------------------------------------------------
125 - (void) readPreferenceFile:(NSNotification *)notification
128 [afsSysPath release];
131 CFPreferencesSynchronize((CFStringRef)kAfsCommanderID, kCFPreferencesAnyUser, kCFPreferencesAnyHost);
132 CFPreferencesSynchronize((CFStringRef)kAfsCommanderID, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
134 afsSysPath = PREFERENCE_AFS_SYS_PAT_STATIC;
136 // read the preference for aklog use
137 useAklogPrefValue = (NSNumber*)CFPreferencesCopyValue((CFStringRef)PREFERENCE_USE_AKLOG,
138 (CFStringRef)kAfsCommanderID,
139 kCFPreferencesCurrentUser,
140 kCFPreferencesAnyHost);
142 showStatusMenu = (NSNumber*)CFPreferencesCopyValue((CFStringRef)PREFERENCE_SHOW_STATUS_MENU,
143 (CFStringRef)kAfsCommanderID,
144 kCFPreferencesCurrentUser,
145 kCFPreferencesAnyHost);
147 aklogTokenAtLogin = (NSNumber*)CFPreferencesCopyValue((CFStringRef)PREFERENCE_AKLOG_TOKEN_AT_LOGIN, (CFStringRef)kAfsCommanderID,
148 kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
151 [self updateAfsStatus:nil];
154 // -------------------------------------------------------------------------------
155 // - (void)chageMenuVisibility:(NSNotification *)notification
156 // -------------------------------------------------------------------------------
157 - (void)chageMenuVisibility:(NSNotification *)notification {
158 [self readPreferenceFile:nil];
159 [self setStatusItem:[showStatusMenu boolValue]];
162 // -------------------------------------------------------------------------------
163 // - (void) switchHandler:(NSNotification*) notification
164 // -------------------------------------------------------------------------------
165 - (void) switchHandler:(NSNotification*) notification
167 if ([[notification name] isEqualToString:NSWorkspaceSessionDidResignActiveNotification])
169 // user has switched out
173 // user has switched in
174 NSLog(@"User has switch in again");
175 if([aklogTokenAtLogin boolValue] && afsState && !gotToken) {
176 NSLog(@"Proceed to get token");
177 //check if we must get the aklog at logint(first run of backgrounder
182 // -------------------------------------------------------------------------------
183 // -(void) afsVolumeMountChange - Track for mount unmount afs volume
184 // -------------------------------------------------------------------------------
185 - (void) afsVolumeMountChange:(NSNotification *)notification{
186 [self updateAfsStatus:nil];
190 // -------------------------------------------------------------------------------
191 // -(void) klogUserEven
192 // -------------------------------------------------------------------------------
193 -(void) klogUserEven:(NSNotification *)notification
195 if(credentialMenuController) {
196 [[NSDistributedNotificationCenter defaultCenter] removeObserver:self name:kAFSMenuExtraID object:kLogWindowClosed];
197 [credentialMenuController closeWindow];
198 [credentialMenuController release];
199 credentialMenuController = nil;
201 //Send notification to PreferencePane
202 [[NSDistributedNotificationCenter defaultCenter] postNotificationName:kAfsCommanderID object:kMenuExtraEventOccured];
204 [self updateAfsStatus:nil];
207 // -------------------------------------------------------------------------------
208 // - (void)startStopAfs:(id)sender
209 // -------------------------------------------------------------------------------
210 - (void)startStopAfs:(id)sender
212 if(!afsSysPath) return;
214 OSStatus status = noErr;
215 NSString *afsdPath = [TaskUtil searchExecutablePath:@"afsd"];
216 NSString *rootHelperApp = nil;
217 BOOL currentAfsState = NO;
220 if(afsdPath == nil) return;
221 currentAfsState = [afsMngr checkAfsStatus];
222 rootHelperApp = [[NSBundle mainBundle] pathForResource:@"afshlp" ofType:@""];
225 [self repairHelperTool];
227 // make the parameter to call the root helper app
228 status = [[AuthUtil shared] autorize];
232 NSMutableString *afsKextPath = [[NSMutableString alloc] initWithCapacity:256];
233 [afsKextPath setString:afsSysPath];
234 [afsKextPath appendString:@"/etc/afs.kext"];
236 const char *stopAfsArgs[] = {"stop_afs", [afsKextPath UTF8String], [afsdPath UTF8String], 0L};
237 [[AuthUtil shared] execUnixCommand:[rootHelperApp UTF8String]
241 const char *startAfsArgs[] = {[[ [NSBundle mainBundle] pathForResource:@"start_afs" ofType:@"sh"] UTF8String], [afsSysPath UTF8String], [afsdPath UTF8String], 0L};
242 [[AuthUtil shared] execUnixCommand:[rootHelperApp UTF8String]
248 @catch (NSException * e) {
252 [[AuthUtil shared] deautorize];
253 [self updateAfsStatus:nil];
254 //Send notification to preferencepane
255 [[NSDistributedNotificationCenter defaultCenter] postNotificationName:kAfsCommanderID object:kMenuExtraEventOccured];
260 // -------------------------------------------------------------------------------
262 // -------------------------------------------------------------------------------
263 - (void)getToken:(id)sender
267 globalRect.origin = [[[statusItem view] window] convertBaseToScreen:[[statusItem view] frame].origin];
268 globalRect.size = [[statusItem view] frame].size;
269 AFSPropertyManager *afsPropMngr = [[AFSPropertyManager alloc] initWithAfsPath:afsSysPath ];
270 [afsPropMngr loadConfiguration];
272 if([useAklogPrefValue boolValue]) {
273 [afsPropMngr getTokens:false
276 [self klogUserEven:nil];
278 // register for user event
279 [[NSDistributedNotificationCenter defaultCenter] addObserver:self
280 selector:@selector(klogUserEven:)
282 object:kLogWindowClosed];
284 credentialMenuController = [[AFSMenuCredentialContoller alloc] initWhitRec:globalRect
285 afsPropManager:afsPropMngr];
286 [credentialMenuController showWindow];
289 //Dispose afs manager
290 [afsPropMngr release];
291 [[NSDistributedNotificationCenter defaultCenter] postNotificationName:kAfsCommanderID
292 object:kMExtraTokenOperation];
295 // -------------------------------------------------------------------------------
296 // -(void) releaseToken
297 // -------------------------------------------------------------------------------
298 - (void)releaseToken:(id)sender
301 [self updateAfsStatus:nil];
302 [[NSDistributedNotificationCenter defaultCenter] postNotificationName:kAfsCommanderID
303 object:kMExtraTokenOperation];
307 // -------------------------------------------------------------------------------
308 // -(void) updateAfsStatus
309 // -------------------------------------------------------------------------------
310 - (void)updateAfsStatus:(NSTimer*)timer
313 if(![tokensLock tryLock]) return;
315 // check the afs state in esclusive mode
316 afsState = [afsMngr checkAfsStatus];
318 NSArray *tokens = [afsMngr getTokenList];
319 gotToken = [tokens count] > 0;
322 //update the menu icon
323 [[statusItem view] setNeedsDisplay:YES];
328 // -------------------------------------------------------------------------------
330 // -------------------------------------------------------------------------------
332 //start the time for check tokens validity
333 if(timerForCheckTokensList) return;
334 timerForCheckTokensList = [NSTimer scheduledTimerWithTimeInterval:TOKENS_REFRESH_TIME_IN_SEC
336 selector:@selector(updateAfsStatus:)
339 [timerForCheckTokensList fire];
342 // -------------------------------------------------------------------------------
344 // -------------------------------------------------------------------------------
346 if(!timerForCheckTokensList) return;
347 [timerForCheckTokensList invalidate];
348 timerForCheckTokensList = nil;
350 // -------------------------------------------------------------------------------
351 // -(void) getImageFromBundle
352 // -------------------------------------------------------------------------------
353 - (NSImage*)getImageFromBundle:(NSString*)fileName fileExt:(NSString*)ext
355 return [[NSImage alloc]initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:fileName
360 // -------------------------------------------------------------------------------
361 // - (void)menuNeedsUpdate:(NSMenu *)menu
362 // -------------------------------------------------------------------------------
363 - (void)menuNeedsUpdate:(NSMenu *)menu {
364 if (menu == backgrounderMenu)
366 [startStopMenuItem setTitle:afsState?@"Shutdown AFS":@"Startup AFS"];
367 [getReleaseTokenMenuItem setTitle:gotToken?@"Release token":@"Get New Token"];
368 [getReleaseTokenMenuItem setEnabled:afsState];
369 [[statusItem view] setNeedsDisplay:YES];
375 // -------------------------------------------------------------------------------
376 // -(void) useAklogPrefValue
377 // -------------------------------------------------------------------------------
378 - (BOOL)useAklogPrefValue
380 if(useAklogPrefValue) return [useAklogPrefValue intValue] == NSOnState;
381 else return NSOffState;
384 // -------------------------------------------------------------------------------
386 // -------------------------------------------------------------------------------
387 - (void) repairHelperTool
392 NSString *afshlpPath = [[NSBundle mainBundle] pathForResource:@"afshlp" ofType:nil];
396 // Open tool exclusively, so nobody can change it while we bless it.
397 fdTool = open([afshlpPath UTF8String], O_NONBLOCK | O_RDONLY | O_EXLOCK, 0);
401 NSLog(@"Exclusive open while repairing tool failed: %d.", errno);
405 if(fstat(fdTool, &st))
407 NSLog(@"fstat failed.");
413 status = [[AuthUtil shared] autorize];
415 fchown(fdTool, 0, st.st_gid);
417 // Disable group and world writability and make setuid root.
418 fchmod(fdTool, (st.st_mode & (~(S_IWGRP | S_IWOTH)))/* | S_ISUID*/);
419 const char *args[] = {"root", [afshlpPath UTF8String],0L};
420 [[AuthUtil shared] execUnixCommand:"/usr/sbin/chown"
423 [[AuthUtil shared] deautorize];
425 } else NSLog(@"st_uid = 0");
427 NSLog(@"Self-repair done.");
431 #pragma mark accessor
432 // -------------------------------------------------------------------------------
434 // -------------------------------------------------------------------------------
435 -(NSStatusItem*)statusItem {
439 // -------------------------------------------------------------------------------
441 // -------------------------------------------------------------------------------
442 -(void)setStatusItem:(BOOL)show {
444 if(statusItem) return;
445 statusItem = [[[NSStatusBar systemStatusBar] statusItemWithLength:menuSize.width] retain];
446 [statusItem setView:[[AFSMenuExtraView alloc] initWithFrame:[[statusItem view] frame]
448 menu:backgrounderMenu]];
449 //Tells the NSStatusItem what menu to load
450 [statusItem setMenu:backgrounderMenu];
451 //Sets the tooptip for our item
452 [statusItem setToolTip:@"OpenAFS Preference"];
453 //Enables highlighting
454 [statusItem setHighlightMode:YES];
455 [statusItem setImage:noTokenImage];
457 if(!statusItem) return;
458 [[NSStatusBar systemStatusBar] removeStatusItem:statusItem];
459 [statusItem autorelease];
463 // -------------------------------------------------------------------------------
464 // -(void) imageToRender
465 // -------------------------------------------------------------------------------
466 - (NSImage*)imageToRender
469 return hasTokenImage;
476 -(IBAction) startStopEvent:(id)sender {
477 [self startStopAfs:sender];
480 -(IBAction) getReleaseTokenEvent:(id)sender {
482 [self releaseToken:sender];
484 [self getToken:sender];