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>
26 #define LINK_ICON 'srvr'
28 @implementation AFSBackgrounderDelegate
29 #pragma mark NSApp Delegate
30 - (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
32 linkCreationLock = [[NSLock alloc] init];
34 afsMngr = [[AFSPropertyManager alloc] initWithAfsPath:PREFERENCE_AFS_SYS_PAT_STATIC];
36 // allocate the lock for concurent afs check state
37 tokensLock = [[NSLock alloc] init];
38 renewTicketLock = [[NSLock alloc] init];
40 //remove the auto eanble on menu item
41 [backgrounderMenu setAutoenablesItems:NO];
42 //Sets the images in our NSStatusItem
46 // Get the imge for menu
47 //Load image for menu rappresentation
48 hasTokenImage = [self getImageFromBundle:@"hasToken"
51 noTokenImage = [self getImageFromBundle:@"noToken"
53 //get the size of the menu icon
54 menuSize = [hasTokenImage size];
56 //inizialize the local link mode status
57 currentLinkActivationStatus = NO;
59 //Start to read the afs path
60 [self readPreferenceFile:nil];
65 // Register for preference user change
66 [[NSDistributedNotificationCenter defaultCenter] addObserver:self
67 selector:@selector(readPreferenceFile:)
68 name:kAFSMenuExtraID object:kPrefChangeNotification];
70 // Register for afs state change
71 [[NSDistributedNotificationCenter defaultCenter] addObserver:self
72 selector:@selector(afsVolumeMountChange:)
73 name:kAFSMenuExtraID object:kMExtraAFSStateChange];
75 // Register for menu state change
76 [[NSDistributedNotificationCenter defaultCenter] addObserver:self
77 selector:@selector(chageMenuVisibility:)
78 name:kAFSMenuExtraID object:kMExtraAFSMenuChangeState];
80 //Register for mount/unmount afs volume
81 [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self
82 selector:@selector(afsVolumeMountChange:)
83 name:NSWorkspaceDidMountNotification object:nil];
85 [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self
86 selector:@selector(afsVolumeMountChange:)
87 name:NSWorkspaceDidUnmountNotification object:nil];
89 [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self
90 selector:@selector(switchHandler:)
91 name:NSWorkspaceSessionDidBecomeActiveNotification
94 [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self
95 selector:@selector(switchHandler:)
96 name:NSWorkspaceSessionDidResignActiveNotification
99 //try to see if we need tho show the menu at startup
101 [self setStatusItem:[showStatusMenu boolValue]];
103 NSLog(@"Check if we need to get token at login time %d", [aklogTokenAtLogin intValue]);
104 if([aklogTokenAtLogin boolValue] && afsState && !gotToken) {
105 NSLog(@"Proceed to get token");
106 //check if we must get the aklog at logint(first run of backgrounder
111 - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender {
112 if(afsSysPath) [afsSysPath release];
116 [self stopTimerRenewTicket];
118 if(hasTokenImage) [hasTokenImage release];
119 if(noTokenImage) [noTokenImage release];
121 // Unregister for preference change
122 [[NSDistributedNotificationCenter defaultCenter] removeObserver:self name:NSWorkspaceSessionDidBecomeActiveNotification object:nil];
123 [[NSDistributedNotificationCenter defaultCenter] removeObserver:self name:NSWorkspaceSessionDidResignActiveNotification object:nil];
124 [[NSDistributedNotificationCenter defaultCenter] removeObserver:self name:kAFSMenuExtraID object:kPrefChangeNotification];
125 [[NSDistributedNotificationCenter defaultCenter] removeObserver:self name:kAFSMenuExtraID object:kMExtraAFSStateChange];
126 [[NSDistributedNotificationCenter defaultCenter] removeObserver:self name:kAFSMenuExtraID object:kMExtraAFSMenuChangeState];
127 [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:self name:NSWorkspaceDidMountNotification object:nil];
128 [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:self name:NSWorkspaceDidUnmountNotification object:nil];
131 // send notify that menuextra has closed
132 [[NSDistributedNotificationCenter defaultCenter] postNotificationName:kAfsCommanderID object:kPrefChangeNotification];
134 if(tokensLock) [tokensLock release];
135 if(afsMngr) [afsMngr release];
136 if(linkCreationLock) [linkCreationLock release];
137 return NSTerminateNow;
139 #pragma mark Notification Handler
140 // -------------------------------------------------------------------------------
141 // -(void) readPreferenceFile
142 // -------------------------------------------------------------------------------
143 - (void) readPreferenceFile:(NSNotification *)notification
146 [afsSysPath release];
149 CFPreferencesSynchronize((CFStringRef)kAfsCommanderID, kCFPreferencesAnyUser, kCFPreferencesAnyHost);
150 CFPreferencesSynchronize((CFStringRef)kAfsCommanderID, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
152 afsSysPath = PREFERENCE_AFS_SYS_PAT_STATIC;
154 // read the preference for aklog use
155 useAklogPrefValue = (NSNumber*)CFPreferencesCopyValue((CFStringRef)PREFERENCE_USE_AKLOG,
156 (CFStringRef)kAfsCommanderID,
157 kCFPreferencesCurrentUser,
158 kCFPreferencesAnyHost);
160 showStatusMenu = (NSNumber*)CFPreferencesCopyValue((CFStringRef)PREFERENCE_SHOW_STATUS_MENU,
161 (CFStringRef)kAfsCommanderID,
162 kCFPreferencesCurrentUser,
163 kCFPreferencesAnyHost);
165 aklogTokenAtLogin = (NSNumber*)CFPreferencesCopyValue((CFStringRef)PREFERENCE_AKLOG_TOKEN_AT_LOGIN, (CFStringRef)kAfsCommanderID, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
167 //get link configuration
168 NSData *prefData = (NSData*)CFPreferencesCopyValue((CFStringRef)PREFERENCE_LINK_CONFIGURATION, (CFStringRef)kAfsCommanderID, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
169 linkConfiguration = (NSMutableDictionary*)[NSPropertyListSerialization propertyListFromData:prefData
170 mutabilityOption:NSPropertyListMutableContainers
172 errorDescription:nil];
174 //get link enabled status
175 NSNumber *linkEnabledStatus = (NSNumber*)CFPreferencesCopyValue((CFStringRef)PREFERENCE_USE_LINK, (CFStringRef)kAfsCommanderID, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
176 [self updateLinkModeStatusWithpreferenceStatus:[linkEnabledStatus boolValue]];
178 //check the user preference for manage the renew
179 krb5CheckRenew = (NSNumber*)CFPreferencesCopyValue((CFStringRef)PREFERENCE_KRB5_CHECK_ENABLE, (CFStringRef)kAfsCommanderID, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
180 krb5RenewTime = (NSNumber*)CFPreferencesCopyValue((CFStringRef)PREFERENCE_KRB5_RENEW_TIME, (CFStringRef)kAfsCommanderID, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
181 krb5RenewCheckTimeInterval = (NSNumber*)CFPreferencesCopyValue((CFStringRef)PREFERENCE_KRB5_RENEW_CHECK_TIME_INTERVALL, (CFStringRef)kAfsCommanderID, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
182 krb5SecToExpireTimeForRenew = (NSNumber*)CFPreferencesCopyValue((CFStringRef)PREFERENCE_KRB5_SEC_TO_EXPIRE_TIME_FOR_RENEW, (CFStringRef)kAfsCommanderID, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
186 [self updateAfsStatus:nil];
188 //stop and start the timer for krb5 renew
189 [self stopTimerRenewTicket];
190 [self startTimerRenewTicket];
193 // -------------------------------------------------------------------------------
194 // - (void) updateLinkModeStatusWithpreferenceStatus:(BOOL)status
195 // -------------------------------------------------------------------------------
196 - (void) updateLinkModeStatusWithpreferenceStatus:(BOOL)status {
197 //exec the link operation on thread
198 [NSThread detachNewThreadSelector:@selector(performLinkOpeartionOnThread:)
200 withObject:[NSNumber numberWithBool:status]];
204 // -------------------------------------------------------------------------------
205 // - (void) performLinkOpenartionOnThread:(id)object
206 // -------------------------------------------------------------------------------
207 - (void) performLinkOpeartionOnThread:(id)object {
208 [linkCreationLock lock];
209 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
210 NSError *error = nil;
212 NSString *linkDstPath = nil;
213 NSString *linkSrcPath = nil;
214 NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDesktopDirectory, NSUserDomainMask, YES);
215 NSString *documentFolderPath = [paths objectAtIndex:0];
216 NSNumber *number = (NSNumber*)object;
217 //NSString *fType = NSFileTypeForHFSTypeCode(LINK_ICON);
218 //NSImage *picture = [[NSWorkspace sharedWorkspace] iconForFileType:fType];
220 BOOL linkSourcePathExist = NO;
221 BOOL linkDestinationPathExist = NO;
223 NSLog(@"updateLinkModeStatusWithpreferenceStatus %d", [number boolValue]);
224 NSEnumerator *keys = [linkConfiguration keyEnumerator];
225 while ((key = [keys nextObject])) {
227 linkSrcPath = [documentFolderPath stringByAppendingPathComponent:key];
228 //afs destionation path
229 linkDstPath = [linkConfiguration objectForKey:key];
230 linkSourcePathExist = [[NSFileManager defaultManager] fileExistsAtPath:linkSrcPath];
231 linkDestinationPathExist = [[NSFileManager defaultManager] fileExistsAtPath:linkDstPath];
233 if([number boolValue]) {
234 if(!linkSourcePathExist) {
235 if(linkDestinationPathExist) {
236 NSLog(@"Creating link \"%@\" to point to \"%@\"", linkSrcPath, linkDstPath);
237 [[NSFileManager defaultManager] createSymbolicLinkAtPath:linkSrcPath
238 withDestinationPath:linkDstPath
241 //Link has been created so i can chnge the icon
242 /* [[NSWorkspace sharedWorkspace] setIcon:picture
245 NSLog(@"Link \"%@\" created", linkSrcPath);
247 NSLog(@"Link Creation Error: %@", [error localizedDescription]);
250 NSLog(@"Deleting Link: %@", linkSrcPath);
251 [[NSFileManager defaultManager] removeItemAtPath:linkSrcPath
255 //the lynk already exist check if the dest path is accesible
256 if(!linkSourcePathExist) {
257 NSLog(@"Deleting Link: %@", linkSrcPath);
258 [[NSFileManager defaultManager] removeItemAtPath:linkSrcPath
264 NSLog(@"Deleting Link: %@", linkSrcPath);
265 [[NSFileManager defaultManager] removeItemAtPath:linkSrcPath
272 currentLinkActivationStatus = [number boolValue];
273 //release thread resource
275 [linkCreationLock unlock];
278 // -------------------------------------------------------------------------------
279 // - (void)chageMenuVisibility:(NSNotification *)notification
280 // -------------------------------------------------------------------------------
281 - (void)chageMenuVisibility:(NSNotification *)notification {
282 [self readPreferenceFile:nil];
283 [self setStatusItem:[showStatusMenu boolValue]];
286 // -------------------------------------------------------------------------------
287 // - (void) switchHandler:(NSNotification*) notification
288 // -------------------------------------------------------------------------------
289 - (void) switchHandler:(NSNotification*) notification
291 if ([[notification name] isEqualToString:NSWorkspaceSessionDidResignActiveNotification])
293 // user has switched out
297 // user has switched in
298 NSLog(@"User has switch in again");
299 if([aklogTokenAtLogin boolValue] && afsState && !gotToken) {
300 NSLog(@"Proceed to get token");
301 //check if we must get the aklog at logint(first run of backgrounder
306 // -------------------------------------------------------------------------------
307 // -(void) afsVolumeMountChange - Track for mount unmount afs volume
308 // -------------------------------------------------------------------------------
309 - (void) afsVolumeMountChange:(NSNotification *)notification{
310 [self updateAfsStatus:nil];
311 [self readPreferenceFile:nil];
315 // -------------------------------------------------------------------------------
316 // -(void) klogUserEven
317 // -------------------------------------------------------------------------------
318 -(void) klogUserEven:(NSNotification *)notification
320 if(credentialMenuController) {
321 [[NSDistributedNotificationCenter defaultCenter] removeObserver:self name:kAFSMenuExtraID object:kLogWindowClosed];
322 [credentialMenuController closeWindow];
323 [credentialMenuController release];
324 credentialMenuController = nil;
326 //Send notification to PreferencePane
327 [[NSDistributedNotificationCenter defaultCenter] postNotificationName:kAfsCommanderID object:kMenuExtraEventOccured];
329 [self updateAfsStatus:nil];
332 // -------------------------------------------------------------------------------
333 // - (void)startStopAfs:(id)sender
334 // -------------------------------------------------------------------------------
335 - (void)startStopAfs:(id)sender
338 BOOL currentAfsState = NO;
339 currentAfsState = [afsMngr checkAfsStatus];
340 // make the parameter to call the root helper app
343 NSLog(@"Shutting down afs");
347 NSLog(@"Starting up afs");
350 }@catch (NSException * e) {
351 NSLog(@"error %@", [e reason]);
353 [self updateAfsStatus:nil];
354 //Send notification to preferencepane
355 [[NSDistributedNotificationCenter defaultCenter] postNotificationName:kAfsCommanderID object:kMenuExtraEventOccured];
360 // -------------------------------------------------------------------------------
362 // -------------------------------------------------------------------------------
363 - (void)getToken:(id)sender
366 if([useAklogPrefValue boolValue]) {
367 [afsMngr getTokens:false
370 [self klogUserEven:nil];
372 globalRect.origin = [[[statusItem view] window] convertBaseToScreen:[[statusItem view] frame].origin];
373 globalRect.size = [[statusItem view] frame].size;
375 // register for user event
376 [[NSDistributedNotificationCenter defaultCenter] addObserver:self
377 selector:@selector(klogUserEven:)
379 object:kLogWindowClosed];
381 credentialMenuController = [[AFSMenuCredentialContoller alloc] initWhitRec:globalRect
382 afsPropManager:afsMngr];
383 [credentialMenuController showWindow];
385 //Dispose afs manager
386 [[NSDistributedNotificationCenter defaultCenter] postNotificationName:kAfsCommanderID
387 object:kMExtraTokenOperation];
390 // -------------------------------------------------------------------------------
391 // -(void) releaseToken
392 // -------------------------------------------------------------------------------
393 - (void)releaseToken:(id)sender
396 [self updateAfsStatus:nil];
397 [[NSDistributedNotificationCenter defaultCenter] postNotificationName:kAfsCommanderID
398 object:kMExtraTokenOperation];
402 // -------------------------------------------------------------------------------
403 // -(void) updateAfsStatus
404 // -------------------------------------------------------------------------------
405 - (void)updateAfsStatus:(NSTimer*)timer
408 if(![tokensLock tryLock]) return;
410 //reload configuration
411 [afsMngr loadConfiguration];
413 // check the afs state in esclusive mode
414 afsState = [afsMngr checkAfsStatus];
416 NSArray *tokens = [afsMngr getTokenList];
417 gotToken = [tokens count] > 0;
420 //update the menu icon
421 [[statusItem view] setNeedsDisplay:YES];
426 // -------------------------------------------------------------------------------
428 // -------------------------------------------------------------------------------
430 //start the time for check tokens validity
431 if(timerForCheckTokensList) return;
432 timerForCheckTokensList = [NSTimer scheduledTimerWithTimeInterval:TOKENS_REFRESH_TIME_IN_SEC
434 selector:@selector(updateAfsStatus:)
437 [timerForCheckTokensList fire];
440 // -------------------------------------------------------------------------------
442 // -------------------------------------------------------------------------------
444 if(!timerForCheckTokensList) return;
445 [timerForCheckTokensList invalidate];
446 timerForCheckTokensList = nil;
449 // -------------------------------------------------------------------------------
450 // startTimerRenewTicket:
451 // -------------------------------------------------------------------------------
452 - (void)startTimerRenewTicket {
453 //start the time for check ticket renew
454 if(timerForCheckRenewTicket || !krb5RenewCheckTimeInterval || ![krb5RenewCheckTimeInterval intValue]) return;
455 NSLog(@"startTimerRenewTicket with sec %d", [krb5RenewCheckTimeInterval intValue]);
456 timerForCheckRenewTicket = [NSTimer scheduledTimerWithTimeInterval:(krb5RenewCheckTimeInterval?[krb5RenewCheckTimeInterval intValue]:PREFERENCE_KRB5_RENEW_CHECK_TIME_INTERVALL_DEFAULT_VALUE)
458 selector:@selector(krb5RenewAction:)
461 [timerForCheckRenewTicket fire];
464 // -------------------------------------------------------------------------------
465 // stopTimerRenewTicket:
466 // -------------------------------------------------------------------------------
467 - (void)stopTimerRenewTicket {
468 NSLog(@"stopTimerRenewTicket");
469 if(!timerForCheckRenewTicket) return;
470 [timerForCheckRenewTicket invalidate];
471 timerForCheckRenewTicket = nil;
474 // -------------------------------------------------------------------------------
476 // -------------------------------------------------------------------------------
477 - (void)krb5RenewAction:(NSTimer*)timer {
479 if(![renewTicketLock tryLock]) return;
480 NSLog(@"krb5RenewAction %@", [NSDate date]);
482 [Krb5Util renewTicket:[krb5SecToExpireTimeForRenew intValue]
483 renewTime:[krb5RenewTime intValue]];
485 @catch (NSException * e) {
488 [renewTicketLock unlock];
494 // -------------------------------------------------------------------------------
495 // -(void) getImageFromBundle
496 // -------------------------------------------------------------------------------
497 - (NSImage*)getImageFromBundle:(NSString*)fileName fileExt:(NSString*)ext
499 return [[NSImage alloc]initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:fileName
504 // -------------------------------------------------------------------------------
505 // - (void)menuNeedsUpdate:(NSMenu *)menu
506 // -------------------------------------------------------------------------------
507 - (void)menuNeedsUpdate:(NSMenu *)menu {
508 if (menu == backgrounderMenu)
510 [startStopMenuItem setTitle:afsState?@"Shutdown AFS":@"Startup AFS"];
511 [getReleaseTokenMenuItem setTitle:gotToken?@"Release token":@"Get New Token"];
512 [getReleaseTokenMenuItem setEnabled:afsState];
513 [[statusItem view] setNeedsDisplay:YES];
519 // -------------------------------------------------------------------------------
520 // -(void) useAklogPrefValue
521 // -------------------------------------------------------------------------------
522 - (BOOL)useAklogPrefValue
524 if(useAklogPrefValue) return [useAklogPrefValue intValue] == NSOnState;
525 else return NSOffState;
528 // -------------------------------------------------------------------------------
530 // -------------------------------------------------------------------------------
531 - (void) repairHelperTool
536 NSString *afshlpPath = [[NSBundle mainBundle] pathForResource:@"afshlp" ofType:nil];
540 // Open tool exclusively, so nobody can change it while we bless it.
541 fdTool = open([afshlpPath UTF8String], O_NONBLOCK | O_RDONLY | O_EXLOCK, 0);
545 NSLog(@"Exclusive open while repairing tool failed: %d.", errno);
549 if(fstat(fdTool, &st))
551 NSLog(@"fstat failed.");
557 status = [[AuthUtil shared] autorize];
559 fchown(fdTool, 0, st.st_gid);
561 // Disable group and world writability and make setuid root.
562 fchmod(fdTool, (st.st_mode & (~(S_IWGRP | S_IWOTH)))/* | S_ISUID*/);
563 const char *args[] = {"root", [afshlpPath UTF8String],0L};
564 [[AuthUtil shared] execUnixCommand:"/usr/sbin/chown"
567 [[AuthUtil shared] deautorize];
569 } else NSLog(@"st_uid = 0");
571 NSLog(@"Self-repair done.");
575 #pragma mark accessor
576 // -------------------------------------------------------------------------------
578 // -------------------------------------------------------------------------------
579 -(NSStatusItem*)statusItem {
583 // -------------------------------------------------------------------------------
585 // -------------------------------------------------------------------------------
586 -(void)setStatusItem:(BOOL)show {
588 if(statusItem) return;
589 statusItem = [[[NSStatusBar systemStatusBar] statusItemWithLength:menuSize.width] retain];
590 [statusItem setView:[[AFSMenuExtraView alloc] initWithFrame:[[statusItem view] frame]
592 menu:backgrounderMenu]];
593 //Tells the NSStatusItem what menu to load
594 [statusItem setMenu:backgrounderMenu];
595 //Sets the tooptip for our item
596 [statusItem setToolTip:@"OpenAFS Preference"];
597 //Enables highlighting
598 [statusItem setHighlightMode:YES];
599 [statusItem setImage:noTokenImage];
601 if(!statusItem) return;
602 [[NSStatusBar systemStatusBar] removeStatusItem:statusItem];
603 [statusItem autorelease];
607 // -------------------------------------------------------------------------------
608 // -(void) imageToRender
609 // -------------------------------------------------------------------------------
610 - (NSImage*)imageToRender
613 return hasTokenImage;
620 -(IBAction) startStopEvent:(id)sender {
621 [self startStopAfs:sender];
624 -(IBAction) getReleaseTokenEvent:(id)sender {
626 [self releaseToken:sender];
628 [self getToken:sender];