OSX AFSBackgrounder fix minor bug
[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
24 @implementation AFSBackgrounderDelegate
25 #pragma mark NSApp Delegate
26 - (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
27         NSLog(@"Init the afs manager");
28         afsMngr = [[AFSPropertyManager alloc] initWithAfsPath:afsSysPath];
29         // allocate the lock for concurent afs check state
30         tokensLock = [[NSLock alloc] init];
31         
32         //remove the auto eanble on menu item
33         [backgrounderMenu setAutoenablesItems:NO];
34     //Sets the images in our NSStatusItem
35         statusItem = nil;
36         
37   
38         // Get the imge for menu
39         //Load image for menu rappresentation
40         hasTokenImage = [self getImageFromBundle:@"hasToken" 
41                                                                          fileExt:@"png"];
42         
43         noTokenImage = [self getImageFromBundle:@"noToken" 
44                                                                         fileExt:@"png"];
45         //get the sazi of the menu icon
46         menuSize = [hasTokenImage size];
47         //Start to read the afs path
48         [self readPreferenceFile:nil];  
49         [self startTimer];
50
51         
52         
53         // Register for preference user change
54         [[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(readPreferenceFile:) 
55                                                                                                                         name:kAFSMenuExtraID object:kPrefChangeNotification];
56         
57         // Register for afs state change
58         [[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(afsVolumeMountChange:) 
59                                                                                                                         name:kAFSMenuExtraID object:kMExtraAFSStateChange];
60         
61         // Register for menu state change
62         [[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(chageMenuVisibility:) 
63                                                                                                                         name:kAFSMenuExtraID object:kMExtraAFSMenuChangeState];
64         
65         //Register for mount/unmount afs volume
66         [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self 
67                                                                                                                    selector:@selector(afsVolumeMountChange:) 
68                                                                                                                            name:NSWorkspaceDidMountNotification object:nil];
69         
70         [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self 
71                                                                                                                    selector:@selector(afsVolumeMountChange:) 
72                                                                                                                            name:NSWorkspaceDidUnmountNotification object:nil];
73         
74         //try to see if we need tho show the menu at startup
75         
76         [self setStatusItem:[showStatusMenu boolValue]];
77         
78         NSLog(@"Check if we need to get token at login time %d", [aklogTokenAtLogin intValue]);
79         if([aklogTokenAtLogin boolValue] && afsState && !gotToken) {
80                 NSLog(@"Proceed to get token");
81                 //check if we must get the aklog at logint(first run of backgrounder
82                 [self getToken:nil];
83         }       
84 }
85
86 - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender {
87         if(afsSysPath) [afsSysPath release];
88         
89         //release the lock
90         [self stopTimer];
91         
92         if(hasTokenImage) [hasTokenImage release];
93         if(noTokenImage) [noTokenImage release];
94         
95         // Unregister for preference change
96         [[NSDistributedNotificationCenter defaultCenter] removeObserver:self name:kAFSMenuExtraID object:kPrefChangeNotification];
97         [[NSDistributedNotificationCenter defaultCenter] removeObserver:self name:kAFSMenuExtraID object:kMExtraAFSStateChange];
98         [[NSDistributedNotificationCenter defaultCenter] removeObserver:self name:kAFSMenuExtraID object:kMExtraAFSMenuChangeState];
99         [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:self name:NSWorkspaceDidMountNotification object:nil];
100         [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:self name:NSWorkspaceDidUnmountNotification object:nil];
101         
102
103         // send notify that menuextra has closed
104         [[NSDistributedNotificationCenter defaultCenter] postNotificationName:kAfsCommanderID object:kPrefChangeNotification];
105         
106         if(tokensLock) [tokensLock release];
107         if(afsMngr) [afsMngr release];
108         return NSTerminateNow;
109 }
110 // -------------------------------------------------------------------------------
111 //  -(void) readPreferenceFile
112 // -------------------------------------------------------------------------------
113 - (void) readPreferenceFile:(NSNotification *)notification
114 {
115         NSLog(@"Reading preference file");      
116         if(afsSysPath) {
117                 [afsSysPath release];
118                 afsSysPath = nil;
119         }
120         CFPreferencesSynchronize((CFStringRef)kAfsCommanderID,  kCFPreferencesAnyUser, kCFPreferencesAnyHost);
121         CFPreferencesSynchronize((CFStringRef)kAfsCommanderID,  kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
122         
123         afsSysPath = PREFERENCE_AFS_SYS_PAT_STATIC;
124         
125         // read the preference for aklog use
126         useAklogPrefValue = (NSNumber*)CFPreferencesCopyValue((CFStringRef)PREFERENCE_USE_AKLOG, 
127                                                                                                                   (CFStringRef)kAfsCommanderID, 
128                                                                                                                   kCFPreferencesCurrentUser, 
129                                                                                                                   kCFPreferencesAnyHost);
130         
131         showStatusMenu = (NSNumber*)CFPreferencesCopyValue((CFStringRef)PREFERENCE_SHOW_STATUS_MENU, 
132                                                                                                            (CFStringRef)kAfsCommanderID, 
133                                                                                                            kCFPreferencesCurrentUser, 
134                                                                                                            kCFPreferencesAnyHost);
135         
136         aklogTokenAtLogin = (NSNumber*)CFPreferencesCopyValue((CFStringRef)PREFERENCE_AKLOG_TOKEN_AT_LOGIN, (CFStringRef)kAfsCommanderID,  
137                                                                                                                                         kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
138
139         //set the menu name
140         [self updateAfsStatus:nil];
141 }
142
143 // -------------------------------------------------------------------------------
144 //  - (void)chageMenuVisibility:(NSNotification *)notification
145 // -------------------------------------------------------------------------------
146 /**/
147 - (void)chageMenuVisibility:(NSNotification *)notification {
148         NSLog(@"chageMenuVisibility");
149         [self readPreferenceFile:nil];
150         NSLog(@"showStatusMenu: %d", [showStatusMenu intValue]);
151         [self setStatusItem:[showStatusMenu boolValue]];
152 }
153
154 // -------------------------------------------------------------------------------
155 //  - (void)startStopAfs:(id)sender
156 // -------------------------------------------------------------------------------
157 - (void)startStopAfs:(id)sender
158 {
159         NSLog(@"startStopAfs: %s",[afsSysPath UTF8String]);
160         if(!afsSysPath) return;
161         
162         OSStatus status = noErr;
163         NSString *afsdPath = [TaskUtil searchExecutablePath:@"afsd"];
164         NSString *rootHelperApp = nil;
165         BOOL currentAfsState = NO;
166         
167         @try {
168                 if(afsdPath == nil) return;
169                 currentAfsState = [afsMngr checkAfsStatus];
170                 rootHelperApp = [[NSBundle mainBundle] pathForResource:@"afshlp" ofType:@""];
171                 
172                 //[startStopScript setString: resourcePath];
173                 //NSLog(@"LAunch repair HelperTool");
174                 //Check helper app
175                 [self repairHelperTool];
176                 
177                 // make the parameter to call the root helper app
178                 status = [[AuthUtil shared] autorize];
179                 if(status == noErr){
180                         if(currentAfsState){
181                                 //shutdown afs
182                                 //NSLog(@"Shutting down afs");
183                                 NSMutableString *afsKextPath = [[NSMutableString alloc] initWithCapacity:256];
184                                 [afsKextPath setString:afsSysPath];
185                                 [afsKextPath appendString:@"/etc/afs.kext"];
186                                 
187                                 //NSLog(@"executeTaskWithAuth");
188                                 const char *stopAfsArgs[] = {"stop_afs", [afsKextPath  UTF8String], [afsdPath UTF8String], 0L};
189                                 [[AuthUtil shared] execUnixCommand:[rootHelperApp UTF8String] 
190                                                                                           args:stopAfsArgs
191                                                                                         output:nil];
192                         } else {
193                                 //NSLog(@"Starting up afs");
194                                 const char *startAfsArgs[] = {[[ [NSBundle mainBundle] pathForResource:@"start_afs" ofType:@"sh"]  UTF8String], [afsSysPath UTF8String], [afsdPath UTF8String], 0L};
195                                 [[AuthUtil shared] execUnixCommand:[rootHelperApp UTF8String] 
196                                                                                           args:startAfsArgs
197                                                                                         output:nil];
198                         }
199                 }
200         }
201         @catch (NSException * e) {
202                 NSLog([e reason]);
203         }
204         @finally {
205                 [[AuthUtil shared] deautorize];
206                 [self updateAfsStatus:nil];
207                 //Send notification to preferencepane
208                 [[NSDistributedNotificationCenter defaultCenter] postNotificationName:kAfsCommanderID object:kMenuExtraEventOccured];
209         }
210         
211 }
212
213 // -------------------------------------------------------------------------------
214 //  -(void) getToken
215 // -------------------------------------------------------------------------------
216 - (void)getToken:(id)sender
217 {
218         
219         NSRect globalRect;
220         globalRect.origin = [[[statusItem view] window] convertBaseToScreen:[[statusItem view] frame].origin];
221         globalRect.size = [[statusItem view] frame].size;
222         AFSPropertyManager *afsPropMngr = [[AFSPropertyManager alloc] initWithAfsPath:afsSysPath ];
223         [afsPropMngr loadConfiguration]; 
224         
225         
226         if([useAklogPrefValue boolValue]) {
227                 NSLog(@"Use Aklog");
228                 [afsPropMngr getTokens:false 
229                                                    usr:nil 
230                                                    pwd:nil];
231                 [self klogUserEven:nil];
232         } else {
233                 NSLog(@"Use Klog");
234                 // register for user event
235                 [[NSDistributedNotificationCenter defaultCenter] addObserver:self 
236                                                                                                                         selector:@selector(klogUserEven:) 
237                                                                                                                                 name:kAFSMenuExtraID 
238                                                                                                                           object:kLogWindowClosed];
239                 
240                 credentialMenuController = [[AFSMenuCredentialContoller alloc] initWhitRec:globalRect 
241                                                                                                                                         afsPropManager:afsPropMngr];
242                 [credentialMenuController showWindow];
243         }
244         
245         //Dispose afs manager
246         [afsPropMngr release];
247 }
248
249 // -------------------------------------------------------------------------------
250 //  -(void) releaseToken
251 // -------------------------------------------------------------------------------
252 - (void)releaseToken:(id)sender
253 {
254         [afsMngr unlog:nil];
255         [self updateAfsStatus:nil];
256 }
257
258
259 // -------------------------------------------------------------------------------
260 //  -(void) afsVolumeMountChange - Track for mount unmount afs volume
261 // -------------------------------------------------------------------------------
262 - (void) afsVolumeMountChange:(NSNotification *)notification{
263         [self updateAfsStatus:nil];
264 }
265
266 // -------------------------------------------------------------------------------
267 //  -(void) updateAfsStatus
268 // -------------------------------------------------------------------------------
269 - (void)updateAfsStatus:(NSTimer*)timer
270 {
271         //Try to locking
272         if(![tokensLock tryLock]) return;
273         
274         // check the afs state in esclusive mode
275         afsState = [afsMngr checkAfsStatus];
276         
277         NSArray *tokens = [afsMngr getTokenList];
278         gotToken = [tokens count] > 0;
279         [tokens release];
280         
281         //update the menu icon
282         [[statusItem view] setNeedsDisplay:YES];
283         //unlock
284         [tokensLock unlock];
285 }
286
287 // -------------------------------------------------------------------------------
288 //  -(void) klogUserEven
289 // -------------------------------------------------------------------------------
290 -(void) klogUserEven:(NSNotification *)notification
291 {
292         if(credentialMenuController) {
293                 [[NSDistributedNotificationCenter defaultCenter] removeObserver:self name:kAFSMenuExtraID object:kLogWindowClosed];
294                 [credentialMenuController closeWindow];
295                 [credentialMenuController release];
296                 credentialMenuController = nil;
297         }
298         //Send notification to PreferencePane
299         [[NSDistributedNotificationCenter defaultCenter] postNotificationName:kAfsCommanderID object:kMenuExtraEventOccured];
300         
301         [self updateAfsStatus:nil];
302 }
303 #pragma mark Operational Function
304 // -------------------------------------------------------------------------------
305 //  startTimer:
306 // -------------------------------------------------------------------------------
307 - (void)startTimer{
308         //start the time for check tokens validity
309         if(timerForCheckTokensList) return;
310         timerForCheckTokensList = [NSTimer scheduledTimerWithTimeInterval:TOKENS_REFRESH_TIME_IN_SEC 
311                                                                                                                            target:self 
312                                                                                                                          selector:@selector(updateAfsStatus:) 
313                                                                                                                          userInfo:nil 
314                                                                                                                           repeats:YES];
315         [timerForCheckTokensList fire]; 
316 }
317
318 // -------------------------------------------------------------------------------
319 //  stopTimer:
320 // -------------------------------------------------------------------------------
321 - (void)stopTimer{
322         if(!timerForCheckTokensList) return;
323         [timerForCheckTokensList invalidate];   
324         timerForCheckTokensList = nil;
325 }
326 // -------------------------------------------------------------------------------
327 //  -(void) getImageFromBundle
328 // -------------------------------------------------------------------------------
329 - (NSImage*)getImageFromBundle:(NSString*)fileName fileExt:(NSString*)ext
330 {
331         return [[NSImage alloc]initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:fileName 
332                                                                                                                                                   ofType:ext]];
333 }
334
335
336 // -------------------------------------------------------------------------------
337 //  - (void)menuNeedsUpdate:(NSMenu *)menu
338 // -------------------------------------------------------------------------------
339 - (void)menuNeedsUpdate:(NSMenu *)menu {
340         if (menu == backgrounderMenu)
341         {
342                 [startStopMenuItem setTitle:afsState?@"Shutdown AFS":@"Startup AFS"];
343                 [getReleaseTokenMenuItem setTitle:gotToken?@"Release token":@"Get New Token"];
344                 [getReleaseTokenMenuItem setEnabled:afsState];
345                 [[statusItem view] setNeedsDisplay:YES];
346                 
347         }
348 }
349
350
351 // -------------------------------------------------------------------------------
352 //  -(void) useAklogPrefValue
353 // -------------------------------------------------------------------------------
354 - (BOOL)useAklogPrefValue
355 {
356         if(useAklogPrefValue) return [useAklogPrefValue intValue] == NSOnState; 
357         else return NSOffState;
358 }
359
360 // -------------------------------------------------------------------------------
361 //  repairHelperTool:
362 // -------------------------------------------------------------------------------
363 - (void) repairHelperTool
364 {
365         struct stat st;
366     int fdTool;
367         int status = 0;
368         NSLog(@"repairHelperTool"); 
369         NSString *afshlpPath = [[NSBundle mainBundle] pathForResource:@"afshlp" ofType:nil];
370         
371         
372     
373     // Open tool exclusively, so nobody can change it while we bless it.
374     fdTool = open([afshlpPath UTF8String], O_NONBLOCK | O_RDONLY | O_EXLOCK, 0);
375     
376     if(fdTool == -1)
377     {
378         NSLog(@"Exclusive open while repairing tool failed: %d.", errno);
379         exit(-1);
380     }
381     
382     if(fstat(fdTool, &st))
383     {
384         NSLog(@"fstat failed.");
385         exit(-1);
386     }
387     
388     if(st.st_uid != 0)
389     {
390                 status = [[AuthUtil shared] autorize];
391                 if(status == noErr){
392                         fchown(fdTool, 0, st.st_gid);
393                         
394                         // Disable group and world writability and make setuid root.
395                         fchmod(fdTool, (st.st_mode & (~(S_IWGRP | S_IWOTH)))/* | S_ISUID*/);
396                         const char *args[] = {"root", [afshlpPath UTF8String],0L};
397                         [[AuthUtil shared] execUnixCommand:"/usr/sbin/chown" 
398                                                                                   args:args
399                                                                                 output:nil];
400                         [[AuthUtil shared] deautorize];
401                 }
402     } else  NSLog(@"st_uid = 0");
403     
404         
405     
406     close(fdTool);
407     
408     NSLog(@"Self-repair done.");
409         
410 }
411
412 #pragma mark accessor
413 // -------------------------------------------------------------------------------
414 //  statusItem
415 // -------------------------------------------------------------------------------
416 -(NSStatusItem*)statusItem {
417                 return statusItem;
418 }
419
420 // -------------------------------------------------------------------------------
421 //  setStatusItem
422 // -------------------------------------------------------------------------------
423 -(void)setStatusItem:(BOOL)show {
424         if(show) {
425                 if(statusItem) return;
426                 statusItem = [[[NSStatusBar systemStatusBar] statusItemWithLength:menuSize.width] retain];
427                 [statusItem setView:[[AFSMenuExtraView alloc] initWithFrame:[[statusItem view] frame]  
428                                                                                                            backgrounder:self
429                                                                                                                            menu:backgrounderMenu]];
430                 //Tells the NSStatusItem what menu to load
431                 [statusItem setMenu:backgrounderMenu];
432                 //Sets the tooptip for our item
433                 [statusItem setToolTip:@"OpenAFS Preference"];
434                 //Enables highlighting
435                 [statusItem setHighlightMode:YES];
436                 [statusItem setImage:noTokenImage];
437         } else {
438                 if(!statusItem) return;
439                 [[NSStatusBar systemStatusBar] removeStatusItem:statusItem];
440                 [statusItem autorelease];
441                 statusItem = nil;
442         }
443 }
444 // -------------------------------------------------------------------------------
445 //  -(void) imageToRender
446 // -------------------------------------------------------------------------------
447 - (NSImage*)imageToRender
448 {
449         if(gotToken){
450                 return hasTokenImage;
451         } else {
452                 return noTokenImage;
453         }
454 }
455
456
457 -(IBAction) startStopEvent:(id)sender {
458         [self startStopAfs:sender];
459 }
460
461 -(IBAction) getReleaseTokenEvent:(id)sender {
462         [self getToken:sender];
463 }
464 @end