Version imprimable du sujet

Cliquez ici pour voir ce sujet dans son format original

Forums MacBidouille _ Technologies Apple _ Plantage d'appli NSTask

Écrit par : Nono95400 20 Aug 2017, 11:03

Bonjour,

Voilà maintenant quelques années que je n'ai pas touché à Xcode ou bien pour de minimes retouches sur des projets perso.

Et venant d'installer Sierra sur mon ordi, j'ai pu constaté que l'appel à un exécutable unix depuis une application via NSTask, faisait planter immédiatement l'app.
J'ai donc réécrit une classe dans mon application pour me passer de cet exécutable.

Mais j'en ai un autre pour lequel je n'ai pas la moindre idée de la façon dont il procède et je ne peux donc pas m'en passer.

Comment faire pour intégrer un exécutable unix dans un projet Xcode sous Mac OS 10.12 pour pouvoir l'appeler depuis l'application avec NSTask ?

Y-aurait-il un système de signature ou bien de niveau d'autorisation nécessaire pour l'application pour exécuter d'autres programmes ?

Merci

PS : l'exécutable appelé depuis le Terminal avec exactement les mêmes arguments... fonctionne très bien.

Écrit par : teddy7545 24 Aug 2017, 22:24

Bonsoir Nono

Tu ne précises pas grand chose sur ton code et un extrait pourrait être utile pour t'aider davantage.
Ou as tu placé ton script ? comment l'appelles tu ? es tu certain des droits d'exécution de ton script (la version qui est exécutée) ?

Je viens de faire un essai avec Xcode sous Sierra et l'appel à NSTask fonctionne parfaitement (sous réserve d'avoir bien donné les droits d'execution à ton script).

Par ailleurs, en utilisant un bloc try comme ci-dessous, ton application ne devrait pas planter et pourra te donner la raison du blocage.

Code
    @try
    {
        NSTask *t = [[NSTask alloc] init];
        ...
    }
    @catch (NSException *e)
    {
        NSLog(@"Expection occurred %@", [e reason]);        
    }

De grandes chances que si le chemin n'est pas correct ou si les droits d'exécution ne sont pas attribués tu trouves le défaut : launch path not accessible.

Écrit par : Jaypee 25 Aug 2017, 06:22

Est-ce l'occasion de jeter un œil à Swift

un "gist" donne un exemple: https://gist.github.com/Seasons7/836d3676884a40c8c98a

Code
    import Foundation
    
    
    let filePath = "/usr/local"

    // Création de la tâche en Swift 3.x
    let task = Process()
    
    // Configuration de la tâche
    task.launchPath = "/bin/ls"
    task.arguments = ["-laF@" , filePath]
    
    // Tuyau de sortie
        let pipe = Pipe()
    task.standardOutput = pipe
    
    // Exécution de la tâche
    task.launch()
    
    // Récupération du résultat
    let data = pipe.fileHandleForReading.readDataToEndOfFile()
    let output = String(data: data, encoding: String.Encoding.utf8)
    
    print(output!)


Et on peut l' essayer sans compiler dans un playground.

J-P

Écrit par : teddy7545 26 Aug 2017, 16:32

Bonjour

Merci Jaypee pour le petit test en Swift ... c'est en effet une bonne occasion de tester un peu plus Swift (surtout maintenant que je viens de passer à Sierra avec Xcode 8)
Evidemment ça fonctionne très bien ... mais même en Swift il faut que le script possède les droits d'exécution .... et à défaut ça fait évidemment planter de la même façon qu'en ObjectiveC....

Dans ton exemple, tu utilises un exécutable du système (qui est évidemment exécutable) mais je pense que Nono95400 souhaitais utiliser un script perso encaqué dans son bundle auquel il doit affecter les droits au bon fichier (celui qui est ajouté dans le répertoire source)

De fait, pour avoir un message d'erreur en clair plutôt qu'un plantage il faudrait également utiliser en Swift un équivalent du bloc try que j'avais moi même mis en place avec ObjectiveC pour comprendre le problème que rencontrait Nono95400.

J'ai trouvé quelques exemples de code Swift qui mettent en oeuvre ce mécanisme et je vais essayer de l'implémenter dans cet exemple avec NSTask.

Écrit par : Jaypee 27 Aug 2017, 08:40

Dans le cas où la commande shell se conforme au standard avec la convention:
status == 0 => success
status != 0 => erreur

on peut récupérer la valeur dans task.terminationStatus un Int32

!!! Attention: ne pas essayer le code suivant avec des privilèges: il tente de supprimer le log du système !!!!

Code
    // import Cocoa // pas certain que ce soit utile
    import Foundation
    
    
    let filePath = "/var/log/system.log"
    ""
    // Création de la tâche en Swift 3.x
    let task = Process()
    
    // Configuration de la tâche
    task.launchPath = "/bin/rm"
    task.arguments = ["-f" , filePath]
    
    // Tuyau de sortie
    let pipe = Pipe()
    task.standardOutput = pipe
    
    // Exécution de la tâche
    task.launch()
    
    task.waitUntilExit()
    
    // Récupération du résultat
    let data = pipe.fileHandleForReading.readDataToEndOfFile()
    let output = String(data: data, encoding: String.Encoding.utf8)
    
    print ("Termination status:  \(task.terminationStatus)")
    print("Output: \(output!)")


On voit que le statut est 1 donc en erreur. Le shell ne lève pas d'exception donc pas de try catch possible.
On voit aussi que output est vide.

J-P

Écrit par : Nono95400 1 Sep 2017, 18:54

Bonjour,

Merci pour votre aide.

Je suis un peu perdu avec Swift, en ce moment pour le boulot j'apprend C#, VBA et ST. Donc pas trop le temps pour apprendre un langage supplémentaire à titre perso.

Je suis tout aussi perdu avec les nouvelles règles de sécurité de Mac OS Sierra, si je comprend bien un exécutable peut ne pas être autorisé à être appelé par une application et en même temps, il peut être lancé sans soucis depuis le Terminal ?

L'exécutable est situé dans le dossier "Resources" de l'application, il y est copié par Xcode lors de la compilation de l'application.

Comment puis-je l'autorisé à être exécuté par mon application ?

Écrit par : Jaypee 2 Sep 2017, 18:58

Salut Nono95400,

Si on vérifie les basiques, les permissions elles sont correctes?

il doit y avoir r et x au minimum partout, 755 ou rwxr-xr-x sur la commande que tu embarques dans l'appli

J-P

Écrit par : Nono95400 5 Sep 2017, 19:20

Bonjour,

Voici les droits de l'exécutable qui est copié dans l'appli lors de la compilation par Xcode : -rwxr-xr-x

Écrit par : teddy7545 8 Sep 2017, 20:44

Bonsoir,

Ton problème est curieux car les droits que tu mentionne sont bien adaptés.
J'ai fais les tests avec Xcode en ObjectiveC comme en Swift (avec la proposition de Jaypee) et tout fonctionne sans problème.

Citation
PS : l'exécutable appelé depuis le Terminal avec exactement les mêmes arguments... fonctionne très bien.
Es tu certain que c'est le même exécutable que tu lances avec Xcode ou avec le terminal (je veux dire pas sa copie située à un autre endroit)

Citation
Voici les droits de l'exécutable qui est copié dans l'appli lors de la compilation par Xcode : -rwxr-xr-x
Es tu certain que Xcode utilise bien cet exécutable ? comment l'appelles tu dans ton code ?

Peux tu montrer le code que tu utilises pour appeler ton exécutable (ou au moins un extrait) ?
Peux tu montrer le défaut que tu obtient ?


Écrit par : Nono95400 9 Sep 2017, 09:23

Bonjour,

Sans rien avoir changé à mon projet ou au code, voilà que l'appli ne plante plus lors de l'appel à l'exécutable huh.gif

Pour info, le bout de code réalisant l'appel :

Code
+ (NSString *)executeGetMonitor:(NSArray*)arguments {
    NSString * strResult=nil;
    NSPipe * thePipe=[[NSPipe alloc] init];
    NSTask * theTask=[[NSTask alloc] init];
    
    NSString * path = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"highmonitor"];
    
    [theTask setLaunchPath: path];
    
    [theTask setArguments: arguments];
    [theTask setStandardOutput: thePipe];
    [theTask launch];
    [theTask waitUntilExit];
    if ([theTask terminationStatus] == 0) {
        strResult = [[NSString alloc] initWithData:[[thePipe fileHandleForReading] readDataToEndOfFile] encoding:NSUTF8StringEncoding];
    } else {
        strResult = [[NSString alloc] initWithFormat:@"Erreur (%d)", [theTask terminationStatus]];
    }
    [theTask release];
    [thePipe release];
    return strResult;
}


Lors de l'exécution, je me rend compte que contrairement à avant (mon passage à Sierra), cela prend plusieurs longues secondes.

Je vais peut-être faire un "delegate" pour récupérer le résultat, une fois qu'il est disponible et éviter de bloquer l'appli.

Propulsé par Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)