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