Programmatisches Abrufen des absoluten Pfads einer OS X-Befehlszeilen-App
Lesezeit: 9 Minuten
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:
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
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 man3dyld 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].
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
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.
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
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.
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
13622500cookie-checkProgrammatisches Abrufen des absoluten Pfads einer OS X-Befehlszeilen-Appyes
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