Programmatisches Abrufen des absoluten Pfads einer OS X-Befehlszeilen-App

Lesezeit: 9 Minuten

Benutzer-Avatar
Benjamin Polack

Unter Linux kann eine Anwendung ihren absoluten Pfad einfach durch Abfragen erhalten /proc/self/exe. Unter FreeBSD ist es komplizierter, da Sie einen sysctl-Aufruf aufbauen müssen:

int mib[4];
mib[0] = CTL_KERN;
mib[1] = KERN_PROC;
mib[2] = KERN_PROC_PATHNAME;
mib[3] = -1;
char buf[1024];
size_t cb = sizeof(buf);
sysctl(mib, 4, buf, &cb, NULL, 0);

aber es ist noch völlig machbar. Ich kann jedoch keine Möglichkeit finden, dies unter OS X für eine Befehlszeilenanwendung zu bestimmen. Wenn Sie innerhalb eines App Bundles ausführen, können Sie dies durch Ausführen ermitteln [[NSBundle mainBundle] bundlePath]aber da Befehlszeilenanwendungen nicht in Bundles enthalten sind, hilft dies nicht.

(Hinweis: Beratung argv[0] ist keine vernünftige Antwort, da, wenn sie von einem Symlink gestartet wird, argv[0] wird dieser symbolische Link sein – nicht der endgültige Pfad zu der aufgerufenen ausführbaren Datei. argv[0] kann auch liegen, wenn eine dumme Anwendung eine verwendet exec() anrufen und vergessen, argv richtig zu initialisieren, was ich in freier Wildbahn gesehen habe.)

  • Lesen argv[0] ist die Lösung und nichts in diesem Thread hat mich bisher überzeugt.

    – Bortzmeyer

    29. April 2009 um 7:49 Uhr

  • @bortzmeyer: Überlege execl("/home/hacker/.hidden/malicious", "/bin/ls", "-s", (char *)0); – der Wert von ‘argv[0]` ist "/bin/ls" aber das hat nichts mit dem Namen der ausführbaren Datei zu tun.

    – Jonathan Leffler

    24. Mai 2011 um 6:23 Uhr

Benutzer-Avatar
mark4o

Die Funktion _NSGetExecutablePath gibt einen vollständigen Pfad zur ausführbaren Datei zurück (GUI oder nicht). Der Pfad kann symbolische Links enthalten, “..” usw. aber die realpath Funktion kann verwendet werden, um diese bei Bedarf zu bereinigen. Sehen man 3 dyld für mehr Informationen.

char path[1024];
uint32_t size = sizeof(path);
if (_NSGetExecutablePath(path, &size) == 0)
    printf("executable path is %s\n", path);
else
    printf("buffer too small; need size %u\n", size);

Das Geheimnis dieser Funktion besteht darin, dass der Darwin-Kernel den ausführbaren Pfad direkt nach dem auf den Prozessstapel legt envp -Array, wenn es den Prozess erstellt. Der Editor für dynamische Links dyld greift dies bei der Initialisierung und hält einen Zeiger darauf. Diese Funktion verwendet diesen Zeiger.

  • Ist die letzte Zeile nicht etwas unterbrochen?

    – ioquatix

    27. Juni 2013 um 14:27 Uhr

  • Es gibt 0 bei Erfolg zurück oder -1, wenn der Puffer nicht groß genug ist, in diesem Fall size wird mit der erforderlichen Größe ausgefüllt. In diesem Fall könnten Sie einen Puffer mit zuweisen malloc().

    – mark4o

    27. Juni 2013 um 15:42 Uhr

  • Dies ist also gleichbedeutend mit der Deklaration einer Hauptdatei mit vier Argumenten – das vierte Argument enthält das sichere argv[0].

    – Nicolas Wilson

    23. Oktober 2013 um 10:30 Uhr

  • Ist der zurückgegebene Pfad nullterminiert?

    – Mäuse

    15. November 2014 um 2:16 Uhr

  • #include <sys/syslimits.h> char path[PATH_MAX+1]; // wäre besser

    – Devon

    13. März 2019 um 3:11 Uhr


Ich glaube, es gibt eine viel elegantere Lösung, die tatsächlich für jede PID funktioniert und auch den absoluten Pfad direkt zurückgibt:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <libproc.h>

int main (int argc, char* argv[])
{
    int ret;
    pid_t pid; 
    char pathbuf[PROC_PIDPATHINFO_MAXSIZE];

    pid = getpid();
    ret = proc_pidpath (pid, pathbuf, sizeof(pathbuf));
    if ( ret <= 0 ) {
        fprintf(stderr, "PID %d: proc_pidpath ();\n", pid);
        fprintf(stderr, "    %s\n", strerror(errno));
    } else {
        printf("proc %d: %s\n", pid, pathbuf);
    }

    return 0;
}

  • Vielen Dank! Eine Sache jedoch, mit OS X 10.8.5 funktionierte dies bei mir nicht ohne: #include

    – ursprünglicher_Benutzername

    10. Oktober 2013 um 20:28 Uhr


  • Die beste Lösung, die ich je bekommen konnte. Sehr saubere Arbeit!

    – Kumpel

    7. Oktober 2016 um 18:12 Uhr

  • Dies impliziert, dass _NSGetExecutablePath nicht für alle PIDs funktioniert – können Sie näher darauf eingehen, wieso? Ich kann nirgendwo eine Referenz finden, die dies besagt.

    – PS

    26. April 2018 um 15:50 Uhr

  • @hyperum tut es _NSGetExecutablePath auch für PID arbeiten? Wenn ja, können Sie ein Beispiel geben, um die akzeptierte Antwort weiter zu verbessern?

    – Treff

    14. April 2020 um 16:27 Uhr

  • @hpm Vielleicht bezog sich Alen nur darauf proc_pidpath kann aufgerufen werden, um Informationen über einen beliebigen Prozess von jedem Prozess abzurufen (da er einen PID-Parameter hat), while _NSGetExecutablePath gibt Ihnen nur Auskunft über den aktuellen Vorgang.

    – Burcea Bogdan Madalin

    27. Dezember 2021 um 16:30 Uhr

Benutzer-Avatar
Brian Campell

Sieht so aus, als ob die Antwort lautet, dass Sie es nicht tun können:

Ich versuche, so etwas wie die Funktionalität von lsof zu erreichen und eine ganze Reihe von Statistiken und Informationen über laufende Prozesse zu sammeln. Wenn lsof nicht so langsam wäre, würde ich gerne dabei bleiben.

Wenn Sie lsof neu implementieren, werden Sie feststellen, dass es langsam ist, weil es viel Arbeit macht.

Ich denke, das liegt nicht wirklich daran, dass lsof im Benutzermodus ist, sondern dass es den Adressraum einer Aufgabe durchsuchen muss, um nach Dingen zu suchen, die von einem externen Pager unterstützt werden. Gibt es eine schnellere Möglichkeit, dies zu tun, wenn ich mich im Kernel befinde?

Nein. lsof ist nicht dumm; es tut, was es tun muss. Wenn Sie nur eine Teilmenge seiner Funktionalität benötigen, sollten Sie in Betracht ziehen, mit der lsof-Quelle (die verfügbar ist) zu beginnen und sie auf Ihre Anforderungen zuzuschneiden.

Aus Neugier ist p_textvp überhaupt verwendet? Es sieht so aus, als wäre es auf das der Eltern eingestellt p_textvp in kern_fork (und dann freigelassen werden??), aber es wird in keinem von ihnen berührt kern_exec‘s Routinen.

p_textvp ist nicht benutzt. In Darwin ist die proc nicht die Wurzel des Adressraums; die aufgabe ist. Es gibt kein Konzept des “vnode” für den Adressraum einer Aufgabe, da er nicht unbedingt anfänglich durch Zuordnen eines solchen gefüllt wird.

Wenn exec p_textvp füllen würde, würde es zu der Annahme führen, dass alle Prozesse von einem vnode unterstützt werden. Dann würden Programmierer annehmen, dass es möglich war, einen Pfad zum vnode zu erhalten, und von dort aus ist es ein kurzer Sprung zu der Annahme, dass der aktuelle Pfad zum vnode der Pfad ist, von dem aus er gestartet wurde, und dass die Textverarbeitung auf der Zeichenfolge erfolgt könnte zum Namen des Anwendungspakets führen, was ohne erhebliche Strafe unmöglich zu garantieren wäre.

Mike Smith, Darwin Drivers-Mailingliste

  • Ich hasse es wirklich, Antworten zu akzeptieren, die sagen: „Das kannst du nicht“, aber dieses Zitat scheint den Nagel in den Sarg meiner Frage sicherlich ziemlich schmerzhaft zu schlagen.

    – Benjamin Pollack

    28. April 2009 um 22:13 Uhr

  • Ja, ich hasste es auch, die Antwort zu geben. Ich verbrachte eine Weile mit einer wilden Gänsejagd und versuchte herauszufinden, wie ich die Informationen aus p_textvp herausholen konnte, bevor ich dies entdeckte.

    – Brian Campell

    28. April 2009 um 22:38 Uhr

Das ist spät, aber [[NSBundle mainBundle] executablePath] funktioniert gut für nicht gebündelte Befehlszeilenprogramme.

Ich denke, es gibt keinen garantierten Weg. Wenn argv[0] ein Symlink ist, dann könnten Sie readlink() verwenden. Wenn der Befehl über den $PATH ausgeführt wird, kann man Folgendes versuchen: search(getenv(“PATH”)), getenv(“_”), dladdr()

  • Das deckt viele Fälle ab, schlägt aber immer noch fehl, wenn Sie von einer Anwendung gestartet wurden, die es versäumt hat, argv zu initialisieren[0] richtig – was aus persönlicher Erfahrung auf eine beunruhigende Anzahl von ihnen zutrifft.

    – Benjamin Pollack

    28. April 2009 um 21:22 Uhr

  • Können Sie ein Beispiel für eine solche Anwendung nennen? Es ist nicht die Anwendung, die argv initialisiert, es ist die libc, und die Anwendung müsste etwas ganz Besonderes tun, um argv zu verschlüsseln[0].

    – Bortzmeyer

    29. April 2009 um 7:48 Uhr

  • Ich kann auf Anhieb kein Beispiel für eine Anwendung geben, die es falsch macht, aber alles, was sie tun müssen, um argv zu vermasseln[0] wird vergessen, es richtig einzustellen, wenn eine Anwendung über einen der exec*-Aufrufe aufgerufen wird. libc würde sich nur einmischen, wenn die Anwendung über system() aufgerufen wird.

    – Benjamin Pollack

    29. April 2009 um 15:16 Uhr

Benutzer-Avatar
Bortzmeyer

Warum nicht einfach realpath(argv[0], actualpath);? Richtig, realpath hat einige Einschränkungen (dokumentiert in der Handbuchseite), aber es handhabt symbolische Links gut. Getestet auf FreeBSD und Linux

    % ls -l foobar 
    lrwxr-xr-x  1 bortzmeyer  bortzmeyer  22 Apr 29 07:39 foobar -> /tmp/get-real-name-exe

    % ./foobar 
    My real path: /tmp/get-real-name-exe
#include <limits.h>
#include <stdlib.h>
#include <stdio.h>
#include <libgen.h>
#include <string.h>
#include <sys/stat.h>

int
main(argc, argv)
    int             argc;
    char          **argv;
{
    char            actualpath[PATH_MAX + 1];

    if (argc > 1) {
        fprintf(stderr, "Usage: %s\n", argv[0]);
        exit(1);
    }
    realpath(argv[0], actualpath);
    fprintf(stdout, "My real path: %s\n", actualpath);
    exit(0);
}

Wenn das Programm über PATH gestartet wird, siehe die Lösung von Pixelbeat.

  • Das deckt viele Fälle ab, schlägt aber immer noch fehl, wenn Sie von einer Anwendung gestartet wurden, die es versäumt hat, argv zu initialisieren[0] richtig – was aus persönlicher Erfahrung auf eine beunruhigende Anzahl von ihnen zutrifft.

    – Benjamin Pollack

    28. April 2009 um 21:22 Uhr

  • Können Sie ein Beispiel für eine solche Anwendung nennen? Es ist nicht die Anwendung, die argv initialisiert, es ist die libc, und die Anwendung müsste etwas ganz Besonderes tun, um argv zu verschlüsseln[0].

    – Bortzmeyer

    29. April 2009 um 7:48 Uhr

  • Ich kann auf Anhieb kein Beispiel für eine Anwendung geben, die es falsch macht, aber alles, was sie tun müssen, um argv zu vermasseln[0] wird vergessen, es richtig einzustellen, wenn eine Anwendung über einen der exec*-Aufrufe aufgerufen wird. libc würde sich nur einmischen, wenn die Anwendung über system() aufgerufen wird.

    – Benjamin Pollack

    29. April 2009 um 15:16 Uhr

http://developer.apple.com/documentation/Carbon/Reference/Process_Manager/Reference/reference.html#//apple_ref/c/func/GetProcessBundleLocation

GetProcessBundleLocation scheint zu funktionieren.

  • Es funktioniert, vorausgesetzt, Ihre Anwendung ist eine GUI-Anwendung, die über den Finder gestartet wird und mit Carbon verknüpft ist. In diesem Fall aber [[NSBundle mainBundle] bundlePath] würde auch funktionieren – und vermeiden, die zu erstellen FSRef und Lokalisieren der Prozessseriennummer.

    – Benjamin Pollack

    15. Mai 2009 um 14:56 Uhr

  • @BenjaminPollack [[NSBundle mainBundle] bundlePath] funktioniert auch für Nicht-UI-Anwendungen, die nur eine einzige Binärdatei sind, solange sie dagegen verlinken Foundation. Wenn Ihre App nur gegen Links verlinkt CoreFoundationkönnen Sie verwenden CFBundle. Alle Bundle-Methoden funktionieren auch für eine einfache Binärdatei, die überhaupt kein Bundle ist, obwohl sie möglicherweise nicht immer nützliche Informationen zurückgeben, aber sie funktionieren, um den ausführbaren Pfad Ihrer Binärdatei zu erhalten.

    – Mecki

    8. September 2015 um 14:56 Uhr


1362250cookie-checkProgrammatisches Abrufen des absoluten Pfads einer OS X-Befehlszeilen-App

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy