source: branches/CoreData/SapphireFrappliance/FRAppliance/SapphireImportHelper.m @ 807

Revision 807, 11.2 KB checked in by gbooker, 5 years ago (diff)

These take longer than the old function, so change it from a fire/and/forget to something that waits.

Line 
1/*
2 * SapphireImportHelper.m
3 * Sapphire
4 *
5 * Created by Graham Booker on Dec. 8, 2007.
6 * Copyright 2007 Sapphire Development Team and/or www.nanopi.net
7 * All rights reserved.
8 *
9 * This program is free software; you can redistribute it and/or modify it under the terms of the GNU
10 * General Public License as published by the Free Software Foundation; either version 3 of the License,
11 * or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
14 * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
15 * Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along with this program; if not,
18 * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19 */
20
21#import <Security/Security.h>
22#include <sys/stat.h>
23#include <sys/mount.h>
24
25#import "SapphireImportHelper.h"
26#import "SapphireMetaData.h"
27#import "SapphireAllImporter.h"
28#import "SapphireXMLFileDataImporter.h"
29#import "SapphireFileDataImporter.h"
30#import "SapphireTVShowImporter.h"
31#import "SapphireMovieImporter.h"
32#import "SapphireFileMetaData.h"
33#import "SapphireMetaDataSupport.h"
34#import "SapphireApplianceController.h"
35
36#define CONNECTION_NAME @"Sapphire Server"
37
38@interface SapphireImportFile : NSObject <SapphireImportFileProtocol>{
39        NSString                                                                *path;
40        id <SapphireImporterBackgroundProtocol> informer;
41        ImportType                                                              type;
42}
43- (id)initWithPath:(NSString *)aPath informer:(id <SapphireImporterBackgroundProtocol>)aInformer type:(ImportType)aType;
44@end
45
46@interface SapphireImportHelperServer (private)
47- (void)startClient;
48@end
49
50@implementation SapphireImportHelper
51
52static SapphireImportHelper *shared = nil;
53
54+ (SapphireImportHelper *)sharedHelperForContext:(NSManagedObjectContext *)moc
55{
56        if(shared == nil)
57                shared = [[SapphireImportHelperServer alloc] initWithContext:moc];
58
59        return shared;
60}
61
62+ (void)relinquishHelper
63{
64        if(shared != nil)
65                [shared relinquishHelper];
66}
67
68- (void)relinquishHelper
69{
70}
71
72- (void)importFileData:(SapphireFileMetaData *)file inform:(id <SapphireImporterBackgroundProtocol>)inform;
73{
74}
75
76- (void)importAllData:(SapphireFileMetaData *)file inform:(id <SapphireImporterBackgroundProtocol>)inform;
77{
78}
79
80- (void)removeObjectsWithInform:(id <SapphireImporterBackgroundProtocol>)inform
81{
82}
83
84@end
85
86@implementation SapphireImportHelperClient
87
88- (id)initWithContext:(NSManagedObjectContext *)context
89{
90        self = [super init];
91        if(!self)
92                return nil;
93       
94        moc = [context retain];
95        SapphireXMLFileDataImporter *xmlImpr = [[SapphireXMLFileDataImporter alloc] init];
96        SapphireFileDataImporter *fileImp = [[SapphireFileDataImporter alloc] init];
97        SapphireTVShowImporter *tvImp = [[SapphireTVShowImporter alloc] initWithContext:moc];
98        SapphireMovieImporter *movImp = [[SapphireMovieImporter alloc] initWithContext:moc];
99        allImporter = [[SapphireAllImporter alloc] initWithImporters:[NSArray arrayWithObjects:xmlImpr,tvImp,movImp,fileImp,nil]];
100        [xmlImpr release];
101        [fileImp release];
102        [tvImp release];
103        [movImp release];
104        keepRunning = YES;
105       
106        return self;
107}
108- (void) dealloc
109{
110        [server release];
111        [allImporter release];
112        [moc release];
113        [super dealloc];
114}
115
116- (void)importFileData:(SapphireFileMetaData *)file inform:(id <SapphireImporterBackgroundProtocol>)inform;
117{
118        updateMetaData(file);
119}
120
121- (void)startChild
122{
123        /*Child here*/
124        @try {
125                NSConnection *connection = [NSConnection connectionWithRegisteredName:CONNECTION_NAME host:nil];
126                id serverobj = [[connection rootProxy] retain];
127                [serverobj setProtocolForProxy:@protocol(SapphireImportServer)];
128                shared = self;
129                [serverobj setClient:(SapphireImportHelperClient *)shared];
130                server = serverobj;     
131                [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(connectionDidDie:) name:NSConnectionDidDieNotification object:nil];           
132        }
133        @catch (NSException * e) {
134                keepRunning = NO;
135        }
136}
137
138- (BOOL)keepRunning
139{
140        return keepRunning;
141}
142
143- (void)connectionDidDie:(NSNotification *)note
144{
145        [self exitChild];
146}
147
148- (oneway void)exitChild
149{
150        keepRunning = NO;
151}
152
153- (void)realStartQueue
154{
155        @try {
156                id <SapphireImportFileProtocol> file;
157                while((file = [server nextFile]) != nil)
158                {
159                        [moc reset];
160                        NSAutoreleasePool *singleImportPool = [[NSAutoreleasePool alloc] init];
161                        ImportType type = [file importType];
162                        BOOL ret;
163                        NSString *path = [file path];
164                        SapphireFileMetaData *file = [SapphireFileMetaData fileWithPath:path inContext:moc];
165                        [moc refreshObject:file mergeChanges:YES];
166                        if(type == IMPORT_TYPE_FILE_DATA)
167                                ret = updateMetaData(file);
168                        else
169                                ret = ([allImporter importMetaData:file path:[file path]] == IMPORT_STATE_UPDATED);
170                        NSDictionary *changes = [SapphireMetaDataSupport changesDictionaryForContext:moc];
171                        [server importCompleteWithChanges:changes updated:ret];
172                        [singleImportPool release];
173                }
174        }
175        @catch (NSException * e) {
176                [SapphireApplianceController logException:e];
177                keepRunning = NO;
178        }
179}
180
181- (oneway void)startQueue
182{
183        [self performSelectorOnMainThread:@selector(realStartQueue) withObject:nil waitUntilDone:NO];
184}
185@end
186
187@implementation SapphireImportHelperServer
188
189- (id)initWithContext:(NSManagedObjectContext *)context
190{
191        self = [super init];
192        if (self == nil)
193                return nil;
194       
195        queue = [[NSMutableArray alloc] init];
196        queueSuspended = NO;
197       
198        serverConnection = [NSConnection defaultConnection];
199        [serverConnection setRootObject:self];
200        if([serverConnection registerName:CONNECTION_NAME] == NO)
201                SapphireLog(SAPPHIRE_LOG_GENERAL, SAPPHIRE_LOG_LEVEL_ERROR, @"Register failed");
202       
203        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(connectionDidDie:) name:NSConnectionDidDieNotification object:nil];
204        moc = [context retain];
205       
206        [self startClient];
207       
208        return self;
209}
210
211- (void) dealloc
212{
213        [[NSNotificationCenter defaultCenter] removeObserver:self];
214        [client release];
215        [moc release];
216        [queue release];
217        [currentImporting release];
218        [super dealloc];
219}
220
221- (void)relinquishHelper
222{
223        [client exitChild];
224        [serverConnection registerName:nil];
225        [serverConnection setRootObject:nil];
226        [shared autorelease];
227        shared = nil;
228}
229
230- (BOOL)isSlashReadOnly
231{
232        struct statfs *mntbufp;
233       
234    int i, mountCount = getmntinfo(&mntbufp, MNT_NOWAIT);
235        for(i=0; i<mountCount; i++)
236        {
237                if(!strcmp(mntbufp[i].f_mntonname, "/"))
238                        return (mntbufp[i].f_flags & MNT_RDONLY) ? YES : NO;
239        }
240       
241        return NO;
242}
243
244- (BOOL)fixClientPermissions:(NSString *)path
245{
246        /* Permissions are incorrect */
247        AuthorizationItem authItems[2] = {
248                {kAuthorizationEnvironmentUsername, strlen("frontrow"), "frontrow", 0},
249                {kAuthorizationEnvironmentPassword, strlen("frontrow"), "frontrow", 0},
250        };
251        AuthorizationEnvironment environ = {2, authItems};
252        AuthorizationItem rightSet[] = {{kAuthorizationRightExecute, 0, NULL, 0}};
253        AuthorizationRights rights = {1, rightSet};
254        AuthorizationRef auth;
255        OSStatus result = AuthorizationCreate(&rights, &environ, kAuthorizationFlagPreAuthorize | kAuthorizationFlagExtendRights, &auth);
256        if(result == errAuthorizationSuccess)
257        {
258                BOOL roslash = [self isSlashReadOnly];
259                if(roslash)
260                {
261                        char *command = "mount -uw /";
262                        char *arguments[] = {"-c", command, NULL};
263                        AuthorizationExecuteWithPrivileges(auth, "/bin/sh", kAuthorizationFlagDefaults, arguments, NULL);
264                }
265                char *command = "chmod +rx \"$HELP\"";
266                setenv("HELP", [path fileSystemRepresentation], 1);
267                char *arguments[] = {"-c", command, NULL};
268                result = AuthorizationExecuteWithPrivileges(auth, "/bin/sh", kAuthorizationFlagDefaults, arguments, NULL);
269                unsetenv("HELP");
270                if(roslash)
271                {
272                        char *command = "mount -ur /";
273                        char *arguments[] = {"-c", command, NULL};
274                        AuthorizationExecuteWithPrivileges(auth, "/bin/sh", kAuthorizationFlagDefaults, arguments, NULL);
275                }
276        }
277        if(result != errAuthorizationSuccess)
278                return NO;
279       
280        return YES;
281}
282
283- (void)startClient
284{
285        NSString *path = [[NSBundle bundleForClass:[SapphireImportHelper class]] pathForResource:@"ImportHelper" ofType:@""];
286        NSDictionary *attrs = [[NSFileManager defaultManager] fileAttributesAtPath:path traverseLink:YES];
287        if(([[attrs objectForKey:NSFilePosixPermissions] intValue] | S_IXOTH) || [self fixClientPermissions:path])
288        {
289                @try {
290                        [NSTask launchedTaskWithLaunchPath:path arguments:[NSArray array]];
291                }
292                @catch (NSException * e) {
293                        SapphireLog(SAPPHIRE_LOG_GENERAL, SAPPHIRE_LOG_LEVEL_ERROR, @"Could not launch helper because of exception %@ launching %@.  Make this file executable", e, path);
294                }               
295        }
296        else
297                SapphireLog(SAPPHIRE_LOG_GENERAL, SAPPHIRE_LOG_LEVEL_ERROR, @"Could not correct helper permissions on %@.  Make this file executable!", path);
298}
299
300- (void)connectionDidDie:(NSNotification *)note
301{
302        [client release];
303        client = nil;
304        /*Inform that import completed (since it crashed, no update done)*/
305        [self importCompleteWithChanges:nil updated:NO];
306        if(shared != nil)
307                /* Don't start it again if we are shutting down*/
308                [self startClient];
309}
310
311- (void)itemAdded
312{
313        if(!queueSuspended)
314                return;
315        queueSuspended = NO;
316        [SapphireMetaDataSupport save:moc];
317        [client startQueue];
318}
319
320- (void)importFileData:(SapphireFileMetaData *)file inform:(id <SapphireImporterBackgroundProtocol>)inform;
321{
322        SapphireImportFile *item = [[SapphireImportFile alloc] initWithPath:[file path] informer:inform  type:IMPORT_TYPE_FILE_DATA];
323        [queue addObject:item];
324        [item release];
325        [self itemAdded];
326}
327
328- (void)importAllData:(SapphireFileMetaData *)file inform:(id <SapphireImporterBackgroundProtocol>)inform;
329{
330        SapphireImportFile *item = [[SapphireImportFile alloc] initWithPath:[file path] informer:inform  type:IMPORT_TYPE_ALL_DATA];
331        [queue addObject:item];
332        [item release];
333        [self itemAdded];
334}
335
336- (void)removeObjectsWithInform:(id <SapphireImporterBackgroundProtocol>)inform
337{
338        if(inform == nil)
339                return;
340       
341        int i, count=[queue count];
342        for(i=0; i<count; i++)
343        {
344                id <SapphireImportFileProtocol> file = [queue objectAtIndex:i];
345                if([file informer] == inform)
346                {
347                        [queue removeObjectAtIndex:i];
348                        i--;
349                        count--;
350                }
351        }
352        if([currentImporting informer] == inform)
353        {
354                [currentImporting release];
355                currentImporting = nil;
356        }
357}
358
359- (id <SapphireImportFileProtocol>)nextFile
360{
361        if(![queue count])
362        {
363                queueSuspended = YES;
364                return nil;
365        }
366        [currentImporting release];
367        currentImporting = [[queue objectAtIndex:0] retain];
368        [queue removeObjectAtIndex:0];
369        return currentImporting;
370}
371
372- (oneway void)setClient:(id <SapphireImportClient>)aClient
373{
374        if(shared == nil)
375        {
376                [aClient exitChild];
377                return;
378        }
379        client = [aClient retain];
380        if([queue count])
381        {
382                queueSuspended = NO;
383                [client startQueue];
384        }
385        else
386                queueSuspended = YES;
387}
388
389- (void)importCompleteWithChanges:(bycopy NSDictionary *)changes updated:(BOOL)updated
390{
391        if(changes != nil)
392                [SapphireMetaDataSupport applyChanges:changes toContext:moc];
393        if(currentImporting == nil)
394                return;
395        [[currentImporting informer] informComplete:updated];
396        [currentImporting release];
397        currentImporting = nil;
398}
399
400@end
401
402@implementation SapphireImportFile
403- (id)initWithPath:(NSString *)aPath informer:(id <SapphireImporterBackgroundProtocol>)aInformer type:(ImportType)aType;
404{
405        self = [super init];
406        if(!self)
407                return nil;
408       
409        path = [aPath retain];
410        informer = [aInformer retain];
411        type = aType;
412       
413        return self;
414}
415- (void) dealloc
416{
417        [path release];
418        [informer release];
419        [super dealloc];
420}
421
422- (bycopy NSString *)path
423{
424        return path;
425}
426- (id <SapphireImporterBackgroundProtocol>)informer
427{
428        return informer;
429}
430
431- (ImportType)importType
432{
433        return type;
434}
435
436@end
Note: See TracBrowser for help on using the repository browser.