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