Search code examples
iosobjective-cuiviewjailbreakdeb

How to show the output from a deb install ios


I'm building a jailbreak app in which it allows deb installs.

I'm stuck on showing the output or install process in another view controller, similar to iFile and Cydia's own installer.

Does anybody know how to get this to show?

What I have so far is a table view, tap file, action sheet pops up asking to install. Pressing install starts the install process and also opens another blank UIView. How would I pass that data to the opened view?

UPDATE 1 as requested:

Code for installing deb and pushing new view controller to display output:

//Deb file extension
NSString *debFileExtension = [fileName pathExtension];
NSLog(@"fileExtension is: %@", externalFileExtension);
NSSet *supportedFileExtensions = [NSSet setWithObjects:@"deb", nil];

if ([supportedFileExtensions containsObject:[debFileExtension lowercaseString]]) {
    documentController = nil;
    NSString *actionSheetTitle = fileName;

    BlockActionSheet *sheet = [BlockActionSheet sheetWithTitle:actionSheetTitle];

    [sheet addButtonWithTitle:@"Install" block:^{

        NSString *appsyncDebPath = [path stringByAppendingPathComponent:fileName];
        NSString *cmdString=[NSString stringWithFormat:@"/usr/bin/dpkg  -i %@",appsyncDebPath];
        const char  *cmdChar=[cmdString UTF8String];
        system(cmdChar);

        DebViewController * vc = [[DebViewController alloc] init];
        [self.navigationController pushViewController:vc animated:YES];
        [vc release];

        NSLog(@"Install pressed %@", cmdString);


    }];

    [sheet setDestructiveButtonWithTitle:@"Cancel" block:nil];
    [sheet showInView:self.view];
}

From that the DebViewController gets called. The issue is the displaying of the output or log or w/e in the new view.

Would a regular UIView work? Or do I need a specific view to receive it?

UPDATE 2: with suggested NSTask.

    NSTask *task1 = [[NSTask alloc] init];
    NSPipe *pipe1 = [NSPipe pipe];
    [task1 setLaunchPath: @"/usr/bin/dpkg"];
    [task1 setArguments: [NSArray arrayWithObjects: @"-i", nil]];
    [task1 setStandardOutput: pipe1];
    [task1 launch];

    NSFileHandle *file = [pipe1 fileHandleForReading];
    NSData * data = [file readDataToEndOfFile];

    NSString * string = [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding];
    NSLog(@"Result: %@", string);



    UITextView *txtview = [[UITextView alloc]initWithFrame:CGRectMake(0, 20, self.view.bounds.size.width, self.view.bounds.size.height)];
    txtview.text = string;
    //some other setup like setting the font for the UITextView...
    [txtview sizeToFit];

    [self.view addSubview:txtview];

UPDATE 3: Everything is working!! Minus 1 thing.

    NSString *debPath = [path stringByAppendingPathComponent:fileName];
    NSTask *task1 = [[NSTask alloc] init];
    NSPipe *pipe1 = [NSPipe pipe];
    [task1 setLaunchPath: @"/Applications/myapp.app/"];
    [task1 setArguments: [NSArray arrayWithObjects: @"/usr/bin/dpkg", @"-i", debPath, nil]];
    [task1 setStandardOutput: pipe1];
    [task1 launch];

    NSFileHandle *file = [pipe1 fileHandleForReading];
    NSData * data = [file readDataToEndOfFile];

    OutputViewController * debOutput = [[OutputViewController alloc] init];
    UINavigationController *vc = [[UINavigationController alloc] initWithRootViewController:debOutput];
    [self.navigationController presentViewController:vc animated:YES completion:nil];
    debOutput.output = [[NSString alloc] initWithData:data encoding: NSUTF8StringEncoding];

So the provided code above works as it should, and the receiving view controller displays the output.

Only thing is, that it's not displaying the full output of the deb install. almost like its shorting the lines. I have the output set up as follows:

UITextView *l = [[UITextView alloc] initWithFrame:CGRectMake(5, 0, self.view.frame.size.width-5, self.view.frame.size.height)];
l.editable = NO;
l.textAlignment = NSTextAlignmentLeft;
l.font=[UIFont boldSystemFontOfSize:14];
l.textColor = [UIColor whiteColor];
l.backgroundColor = [UIColor colorWithWhite:0.1f alpha:1.0f];
l.text = [NSString stringWithFormat: @"%@", output];
l.textContainer.lineBreakMode = NSLineBreakByWordWrapping;
[l release];

UPDATE 4: So what I ended up doing was loading some text in the viewDidLoad when the view first showed:

NSString *cmd0 = @"Running Debian Packager";
NSString *cmd1 = @"Executing Command: /usr/bin/dpkg -i";
NSString *cmd2 = @"Preparing - ";
NSString *cmd3 = @"Installing......Please wait...";

l.text = [NSString stringWithFormat:@"%@\n\n%@\n\n%@%@\n\n%@", cmd0, cmd1, cmd2, fileName, cmd3];
l.textContainer.lineBreakMode = NSLineBreakByWordWrapping;

[view addSubview:l];

Then called the deb install process in the viewDidAppear, which replaces the above code with the output:

//NSTask
NSString *debPath = [path stringByAppendingPathComponent:vc.fileName1];

NSTask *task1 = [[[NSTask alloc] init] autorelease];
NSPipe *pipe1 = [NSPipe pipe];
[task1 setLaunchPath: @"/Applications/myapp.app/process"];
[task1 setArguments: [NSArray arrayWithObjects:@"/usr/bin/dpkg", @"-i", debPath, @"2>/tmp/dpkg.log" ,nil]];
[task1 setStandardOutput: pipe1];
[task1 launch];

NSFileHandle *file = [pipe1 fileHandleForReading];
NSData *data = [file readDataToEndOfFile];

NSString *string = [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease];


NSString *cmd3 = @"Success";
NSString *cmd4 = @"*If this package requires a respring, please do so above*";

NSString *dependencies = @"*You are seeing this message because this package requires the additional dependencies listed above*";
NSString *closeCydia = @"*If you are seeing this message, Cydia is probably open. Please close, and try to install the package again*";

//DPKG LOG FILE
NSError *error;
NSString *logPath = [NSString stringWithFormat:@"/tmp"];
NSString *dpkgLogFile = [logPath stringByAppendingPathComponent:@"dpkg.log"];
NSString *logContents = [NSString stringWithContentsOfFile:dpkgLogFile encoding:NSUTF8StringEncoding error:&error];

NSString *dependsString = @"dpkg: dependency problems";
NSString *lockString = @"dpkg: status database area is locked by another process";

if ([logContents containsString:lockString]) {
    l.text = [NSString stringWithFormat:@"%@\n%@", logContents, closeCydia];
    self.navigationController.navigationBar.topItem.rightBarButtonItem = nil;

}else if ([logContents containsString:dependsString]){
    l.text = [NSString stringWithFormat:@"%@\n%@\n%@", string, logContents, dependencies];
    self.navigationController.navigationBar.topItem.rightBarButtonItem = nil;

}else{
    l.text = [NSString stringWithFormat:@"%@\n%@%@\n\n%@", string, logContents, cmd3, cmd4];
}

[view addSubview:l];

Depending on what the deb installs, I customized the output, i.e. If it has depends, or if the process is locked because Cydia is open.

All in all I'm happy with the turnout. Thanks to Nate for the direction to use NSTask, worked like a charm.

The only thing to make it better is to have it print off or readout, similar to how Cydia goes through line by line.


Solution

  • Instead of using the system() command to run the dpkg command line, I would suggest using NSTask to run the command, which makes it easier to capture the output as a NSString. Once you have a string, you can copy it into a text view, or wherever you like.

    NSTask is a private API on iOS, but it's public on OS X, so there's lots of documentation available. In order to use it in your project, just find a copy of the NSTask.h header and copy it into your project (and #import it, of course).

    Here's an example of using NSTask to capture command line output in a UIApplication.

    Or, another one.

    If your install process may take a while, and you'd like your UI to be responsive while it's running, it would be a good idea to run the method that performs the task in the background (using GCD, for example), and then write the resulting string to your UIView (text field, etc.) back on the main/UI thread.