Develop Kerberos renew system for ticket
[openafs.git] / src / platform / DARWIN / AFSPreference / AFSBackgrounder / AFSBackgrounderDelegate.m
1 //
2 //  AFSBackgrounder.m
3 //  OpenAFS
4 //
5 //  Created by Claudio Bisegni on 29/07/09.
6 //  Copyright 2009 Infn. All rights reserved.
7 //
8
9 #import "AFSBackgrounderDelegate.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 #import <sys/xattr.h>
24 #import "Krb5Util.h"
25
26 #define LINK_ICON 'srvr'
27
28 @implementation AFSBackgrounderDelegate
29 #pragma mark NSApp Delegate
30 - (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
31         
32         linkCreationLock = [[NSLock alloc] init];
33         
34         afsMngr = [[AFSPropertyManager alloc] initWithAfsPath:PREFERENCE_AFS_SYS_PAT_STATIC];
35         
36         // allocate the lock for concurent afs check state
37         tokensLock = [[NSLock alloc] init];
38         renewTicketLock = [[NSLock alloc] init];
39         
40         //remove the auto eanble on menu item
41         [backgrounderMenu setAutoenablesItems:NO];
42     //Sets the images in our NSStatusItem
43         statusItem = nil;
44         
45   
46         // Get the imge for menu
47         //Load image for menu rappresentation
48         hasTokenImage = [self getImageFromBundle:@"hasToken" 
49                                                                          fileExt:@"png"];
50         
51         noTokenImage = [self getImageFromBundle:@"noToken" 
52                                                                         fileExt:@"png"];
53         //get the size of the menu icon
54         menuSize = [hasTokenImage size];
55         
56         //inizialize the local link mode status
57         currentLinkActivationStatus = NO;
58         
59         //Start to read the afs path
60         [self readPreferenceFile:nil];  
61         [self startTimer];
62
63         
64         
65         // Register for preference user change
66         [[NSDistributedNotificationCenter defaultCenter] addObserver:self
67                                                                                                                 selector:@selector(readPreferenceFile:)
68                                                                                                                         name:kAFSMenuExtraID object:kPrefChangeNotification];
69         
70         // Register for afs state change
71         [[NSDistributedNotificationCenter defaultCenter] addObserver:self
72                                                                                                                 selector:@selector(afsVolumeMountChange:)
73                                                                                                                         name:kAFSMenuExtraID object:kMExtraAFSStateChange];
74         
75         // Register for menu state change
76         [[NSDistributedNotificationCenter defaultCenter] addObserver:self
77                                                                                                                 selector:@selector(chageMenuVisibility:)
78                                                                                                                         name:kAFSMenuExtraID object:kMExtraAFSMenuChangeState];
79         
80         //Register for mount/unmount afs volume
81         [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self
82                                                                                                                    selector:@selector(afsVolumeMountChange:)
83                                                                                                                            name:NSWorkspaceDidMountNotification object:nil];
84         
85         [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self
86                                                                                                                    selector:@selector(afsVolumeMountChange:)
87                                                                                                                            name:NSWorkspaceDidUnmountNotification object:nil];
88         
89         [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self
90                                                                                                                    selector:@selector(switchHandler:)
91                                                                                                                            name:NSWorkspaceSessionDidBecomeActiveNotification
92                                                                                                                          object:nil];
93         
94     [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self
95                                                                                                                    selector:@selector(switchHandler:)
96                                                                                                                            name:NSWorkspaceSessionDidResignActiveNotification
97                                                                                                                          object:nil];
98         
99         //try to see if we need tho show the menu at startup
100         
101         [self setStatusItem:[showStatusMenu boolValue]];
102         
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
107                 [self getToken:nil];
108         }       
109 }
110
111 - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender {
112         if(afsSysPath) [afsSysPath release];
113         
114         //release the lock
115         [self stopTimer];
116         [self stopTimerRenewTicket];
117         
118         if(hasTokenImage) [hasTokenImage release];
119         if(noTokenImage) [noTokenImage release];
120         
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];
129         
130
131         // send notify that menuextra has closed
132         [[NSDistributedNotificationCenter defaultCenter] postNotificationName:kAfsCommanderID object:kPrefChangeNotification];
133         
134         if(tokensLock) [tokensLock release];
135         if(afsMngr) [afsMngr release];
136         if(linkCreationLock) [linkCreationLock release];
137         return NSTerminateNow;
138 }
139 #pragma mark Notification Handler
140 // -------------------------------------------------------------------------------
141 //  -(void) readPreferenceFile
142 // -------------------------------------------------------------------------------
143 - (void) readPreferenceFile:(NSNotification *)notification
144 {
145         if(afsSysPath) {
146                 [afsSysPath release];
147                 afsSysPath = nil;
148         }
149         CFPreferencesSynchronize((CFStringRef)kAfsCommanderID, kCFPreferencesAnyUser, kCFPreferencesAnyHost);
150         CFPreferencesSynchronize((CFStringRef)kAfsCommanderID, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
151         
152         afsSysPath = PREFERENCE_AFS_SYS_PAT_STATIC;
153         
154         // read the preference for aklog use
155         useAklogPrefValue = (NSNumber*)CFPreferencesCopyValue((CFStringRef)PREFERENCE_USE_AKLOG,
156                                                                                                                   (CFStringRef)kAfsCommanderID,
157                                                                                                                   kCFPreferencesCurrentUser,
158                                                                                                                   kCFPreferencesAnyHost);
159         
160         showStatusMenu = (NSNumber*)CFPreferencesCopyValue((CFStringRef)PREFERENCE_SHOW_STATUS_MENU,
161                                                                                                            (CFStringRef)kAfsCommanderID,
162                                                                                                            kCFPreferencesCurrentUser,
163                                                                                                            kCFPreferencesAnyHost);
164         
165         aklogTokenAtLogin = (NSNumber*)CFPreferencesCopyValue((CFStringRef)PREFERENCE_AKLOG_TOKEN_AT_LOGIN, (CFStringRef)kAfsCommanderID, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
166
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
171                                                                                                                                                                                  format:nil
172                                                                                                                                                            errorDescription:nil];
173         
174         //get link enabled status
175         NSNumber *linkEnabledStatus =  (NSNumber*)CFPreferencesCopyValue((CFStringRef)PREFERENCE_USE_LINK, (CFStringRef)kAfsCommanderID, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
176         [self updateLinkModeStatusWithpreferenceStatus:[linkEnabledStatus boolValue]];
177         
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);
183
184         
185         //set the menu name
186         [self updateAfsStatus:nil];
187
188                 //stop and start the timer for krb5 renew
189         [self stopTimerRenewTicket];
190         [self startTimerRenewTicket];
191 }
192
193 // -------------------------------------------------------------------------------
194 //  - (void) updateLinkModeStatusWithpreferenceStatus:(BOOL)status
195 // -------------------------------------------------------------------------------
196 - (void) updateLinkModeStatusWithpreferenceStatus:(BOOL)status {
197         //exec the link operation on thread
198                 [NSThread detachNewThreadSelector:@selector(performLinkOpeartionOnThread:)
199                                                          toTarget:self
200                                                    withObject:[NSNumber numberWithBool:status]];
201 }
202
203
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;
211         NSString *key = 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];
219         
220         BOOL linkSourcePathExist = NO;
221         BOOL linkDestinationPathExist = NO;
222         
223         NSLog(@"updateLinkModeStatusWithpreferenceStatus %d", [number boolValue]);
224         NSEnumerator *keys = [linkConfiguration keyEnumerator];
225         while ((key = [keys nextObject])) {
226                 //link path
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];
232                 
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
239                                                                                                                                            error:&error];
240                                         if(!error) {
241                                                 //Link has been created so i can chnge the icon
242                                         /*      [[NSWorkspace sharedWorkspace] setIcon:picture
243                                                                                                            forFile:linkName
244                                                                                                            options:0];*/
245                                                 NSLog(@"Link \"%@\" created", linkSrcPath);
246                                         } else {
247                                                 NSLog(@"Link Creation Error: %@", [error localizedDescription]);
248                                         }
249                                 } else {
250                                         NSLog(@"Deleting Link: %@", linkSrcPath);
251                                         [[NSFileManager defaultManager] removeItemAtPath:linkSrcPath
252                                                                                                                            error:&error];
253                                 }
254                         } else {
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
259                                                                                                                            error:&error];
260                                 }
261                         }
262                 } else {
263                         //delete the link
264                         NSLog(@"Deleting Link: %@", linkSrcPath);
265                         [[NSFileManager defaultManager] removeItemAtPath:linkSrcPath
266                                                                                                            error:&error];
267                         
268                 }
269         }
270         
271         //update the status
272         currentLinkActivationStatus = [number boolValue];
273         //release thread resource
274         [pool release];
275         [linkCreationLock unlock];
276 }
277
278 // -------------------------------------------------------------------------------
279 //  - (void)chageMenuVisibility:(NSNotification *)notification
280 // -------------------------------------------------------------------------------
281 - (void)chageMenuVisibility:(NSNotification *)notification {
282         [self readPreferenceFile:nil];
283         [self setStatusItem:[showStatusMenu boolValue]];
284 }
285
286 // -------------------------------------------------------------------------------
287 // - (void) switchHandler:(NSNotification*) notification
288 // -------------------------------------------------------------------------------
289 - (void) switchHandler:(NSNotification*) notification
290 {
291     if ([[notification name] isEqualToString:NSWorkspaceSessionDidResignActiveNotification])
292     {
293         // user has switched out
294     }
295     else
296     {
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
302                         [self getToken:nil];
303                 }
304     }
305 }
306 // -------------------------------------------------------------------------------
307 //  -(void) afsVolumeMountChange - Track for mount unmount afs volume
308 // -------------------------------------------------------------------------------
309 - (void) afsVolumeMountChange:(NSNotification *)notification{
310         [self updateAfsStatus:nil];
311         [self readPreferenceFile:nil];
312 }
313
314
315 // -------------------------------------------------------------------------------
316 //  -(void) klogUserEven
317 // -------------------------------------------------------------------------------
318 -(void) klogUserEven:(NSNotification *)notification
319 {
320         if(credentialMenuController) {
321                 [[NSDistributedNotificationCenter defaultCenter] removeObserver:self name:kAFSMenuExtraID object:kLogWindowClosed];
322                 [credentialMenuController closeWindow];
323                 [credentialMenuController release];
324                 credentialMenuController = nil;
325         }
326         //Send notification to PreferencePane
327         [[NSDistributedNotificationCenter defaultCenter] postNotificationName:kAfsCommanderID object:kMenuExtraEventOccured];
328         
329         [self updateAfsStatus:nil];
330 }
331 #pragma mark Action
332 // -------------------------------------------------------------------------------
333 //  - (void)startStopAfs:(id)sender
334 // -------------------------------------------------------------------------------
335 - (void)startStopAfs:(id)sender
336 {
337         @try {
338                 BOOL currentAfsState = NO;
339                 currentAfsState = [afsMngr checkAfsStatus];
340                 // make the parameter to call the root helper app
341                 if(currentAfsState){
342                         //shutdown afs
343                         NSLog(@"Shutting down afs");
344                         [afsMngr shutdown];
345                 } else {
346                         //Start afs
347                         NSLog(@"Starting up afs");
348                         [afsMngr startup];
349                 }
350         }@catch (NSException * e) {
351                 NSLog(@"error %@", [e reason]);
352         }@finally {
353                 [self updateAfsStatus:nil];
354                 //Send notification to preferencepane
355                 [[NSDistributedNotificationCenter defaultCenter] postNotificationName:kAfsCommanderID object:kMenuExtraEventOccured];
356         }
357         
358 }
359
360 // -------------------------------------------------------------------------------
361 //  -(void) getToken
362 // -------------------------------------------------------------------------------
363 - (void)getToken:(id)sender
364 {
365         NSRect globalRect;
366         if([useAklogPrefValue boolValue]) {
367                 [afsMngr getTokens:false
368                                            usr:nil
369                                            pwd:nil];
370                 [self klogUserEven:nil];
371         } else {
372                 globalRect.origin = [[[statusItem view] window] convertBaseToScreen:[[statusItem view] frame].origin];
373                 globalRect.size = [[statusItem view] frame].size;
374                 
375                 // register for user event
376                 [[NSDistributedNotificationCenter defaultCenter] addObserver:self
377                                                                                                                         selector:@selector(klogUserEven:)
378                                                                                                                                 name:kAFSMenuExtraID
379                                                                                                                           object:kLogWindowClosed];
380                 
381                 credentialMenuController = [[AFSMenuCredentialContoller alloc] initWhitRec:globalRect
382                                                                                                                                         afsPropManager:afsMngr];
383                 [credentialMenuController showWindow];
384         }
385         //Dispose afs manager
386         [[NSDistributedNotificationCenter defaultCenter] postNotificationName:kAfsCommanderID
387                                                                                                                                    object:kMExtraTokenOperation];
388 }
389
390 // -------------------------------------------------------------------------------
391 //  -(void) releaseToken
392 // -------------------------------------------------------------------------------
393 - (void)releaseToken:(id)sender
394 {
395         [afsMngr unlog:nil];
396         [self updateAfsStatus:nil];
397         [[NSDistributedNotificationCenter defaultCenter] postNotificationName:kAfsCommanderID 
398                                                                                                                                    object:kMExtraTokenOperation];
399 }
400
401
402 // -------------------------------------------------------------------------------
403 //  -(void) updateAfsStatus
404 // -------------------------------------------------------------------------------
405 - (void)updateAfsStatus:(NSTimer*)timer
406 {
407         //Try to locking
408         if(![tokensLock tryLock]) return;
409         
410         //reload configuration
411         [afsMngr loadConfiguration];
412         
413         // check the afs state in esclusive mode
414         afsState = [afsMngr checkAfsStatus];
415         
416         NSArray *tokens = [afsMngr getTokenList];
417         gotToken = [tokens count] > 0;
418         [tokens release];
419         
420         //update the menu icon
421         [[statusItem view] setNeedsDisplay:YES];
422         //unlock
423         [tokensLock unlock];
424 }
425
426 // -------------------------------------------------------------------------------
427 //  startTimer:
428 // -------------------------------------------------------------------------------
429 - (void)startTimer{
430         //start the time for check tokens validity
431         if(timerForCheckTokensList) return;
432         timerForCheckTokensList = [NSTimer scheduledTimerWithTimeInterval:TOKENS_REFRESH_TIME_IN_SEC 
433                                                                                                                            target:self 
434                                                                                                                          selector:@selector(updateAfsStatus:) 
435                                                                                                                          userInfo:nil 
436                                                                                                                           repeats:YES];
437         [timerForCheckTokensList fire]; 
438 }
439
440 // -------------------------------------------------------------------------------
441 //  stopTimer:
442 // -------------------------------------------------------------------------------
443 - (void)stopTimer{
444         if(!timerForCheckTokensList) return;
445         [timerForCheckTokensList invalidate];   
446         timerForCheckTokensList = nil;
447 }
448
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)
457                                                                                                                            target:self
458                                                                                                                          selector:@selector(krb5RenewAction:)
459                                                                                                                          userInfo:nil
460                                                                                                                           repeats:YES];
461         [timerForCheckRenewTicket fire];
462 }
463
464 // -------------------------------------------------------------------------------
465 //  stopTimerRenewTicket:
466 // -------------------------------------------------------------------------------
467 - (void)stopTimerRenewTicket {
468         NSLog(@"stopTimerRenewTicket");
469         if(!timerForCheckRenewTicket) return;
470         [timerForCheckRenewTicket invalidate];
471         timerForCheckRenewTicket = nil;
472 }
473
474 // -------------------------------------------------------------------------------
475 //  krb5RenewAction:
476 // -------------------------------------------------------------------------------
477 - (void)krb5RenewAction:(NSTimer*)timer {
478         //Try to locking
479         if(![renewTicketLock tryLock]) return;
480         NSLog(@"krb5RenewAction %@", [NSDate date]);
481         @try {
482                 [Krb5Util renewTicket:[krb5SecToExpireTimeForRenew intValue]
483                                         renewTime:[krb5RenewTime intValue]];
484         }
485         @catch (NSException * e) {
486         }
487         @finally {
488                 [renewTicketLock unlock];
489         }
490
491
492 }
493
494 // -------------------------------------------------------------------------------
495 //  -(void) getImageFromBundle
496 // -------------------------------------------------------------------------------
497 - (NSImage*)getImageFromBundle:(NSString*)fileName fileExt:(NSString*)ext
498 {
499         return [[NSImage alloc]initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:fileName 
500                                                                                                                                                   ofType:ext]];
501 }
502
503
504 // -------------------------------------------------------------------------------
505 //  - (void)menuNeedsUpdate:(NSMenu *)menu
506 // -------------------------------------------------------------------------------
507 - (void)menuNeedsUpdate:(NSMenu *)menu {
508         if (menu == backgrounderMenu)
509         {
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];
514                 
515         }
516 }
517
518
519 // -------------------------------------------------------------------------------
520 //  -(void) useAklogPrefValue
521 // -------------------------------------------------------------------------------
522 - (BOOL)useAklogPrefValue
523 {
524         if(useAklogPrefValue) return [useAklogPrefValue intValue] == NSOnState; 
525         else return NSOffState;
526 }
527
528 // -------------------------------------------------------------------------------
529 //  repairHelperTool:
530 // -------------------------------------------------------------------------------
531 - (void) repairHelperTool
532 {
533         struct stat st;
534     int fdTool;
535         int status = 0; 
536         NSString *afshlpPath = [[NSBundle mainBundle] pathForResource:@"afshlp" ofType:nil];
537         
538         
539     
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);
542     
543     if(fdTool == -1)
544     {
545         NSLog(@"Exclusive open while repairing tool failed: %d.", errno);
546         exit(-1);
547     }
548     
549     if(fstat(fdTool, &st))
550     {
551         NSLog(@"fstat failed.");
552         exit(-1);
553     }
554     
555     if(st.st_uid != 0)
556     {
557                 status = [[AuthUtil shared] autorize];
558                 if(status == noErr){
559                         fchown(fdTool, 0, st.st_gid);
560                         
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" 
565                                                                                   args:args
566                                                                                 output:nil];
567                         [[AuthUtil shared] deautorize];
568                 }
569     } else  NSLog(@"st_uid = 0");
570     close(fdTool);
571     NSLog(@"Self-repair done.");
572         
573 }
574
575 #pragma mark accessor
576 // -------------------------------------------------------------------------------
577 //  statusItem
578 // -------------------------------------------------------------------------------
579 -(NSStatusItem*)statusItem {
580                 return statusItem;
581 }
582
583 // -------------------------------------------------------------------------------
584 //  setStatusItem
585 // -------------------------------------------------------------------------------
586 -(void)setStatusItem:(BOOL)show {
587         if(show) {
588                 if(statusItem) return;
589                 statusItem = [[[NSStatusBar systemStatusBar] statusItemWithLength:menuSize.width] retain];
590                 [statusItem setView:[[AFSMenuExtraView alloc] initWithFrame:[[statusItem view] frame]  
591                                                                                                            backgrounder:self
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];
600         } else {
601                 if(!statusItem) return;
602                 [[NSStatusBar systemStatusBar] removeStatusItem:statusItem];
603                 [statusItem autorelease];
604                 statusItem = nil;
605         }
606 }
607 // -------------------------------------------------------------------------------
608 //  -(void) imageToRender
609 // -------------------------------------------------------------------------------
610 - (NSImage*)imageToRender
611 {
612         if(gotToken){
613                 return hasTokenImage;
614         } else {
615                 return noTokenImage;
616         }
617 }
618
619
620 -(IBAction) startStopEvent:(id)sender {
621         [self startStopAfs:sender];
622 }
623
624 -(IBAction) getReleaseTokenEvent:(id)sender {
625         if(gotToken)
626                 [self releaseToken:sender];
627         else
628                 [self getToken:sender];
629 }
630 @end