2009-04-28 5 views
18

В Linux приложение может легко получить свой абсолютный путь, запросив /proc/self/exe. Во FreeBSD, это сложнее, так как вы должны построить SYSCTL вызов:Программно получить абсолютный путь для приложения командной строки OS X

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); 

, но это все-таки вполне выполнимо. Однако я не могу найти способ определить это на OS X для приложения с командной строкой. Если вы работаете из пакета приложений, вы можете определить его, выполнив [[NSBundle mainBundle] bundlePath], но поскольку приложения с командной строкой не входят в пакеты, это не поможет.

(Примечание: консультирование argv[0] не является разумным ответом, так как, если запущена из линка, argv[0] будет что символический - не окончательный путем к исполняемому файлу под названием argv[0] также может лежать, если немое приложение использует exec(). позвоните и забудьте правильно инициализировать argv, что я видел в дикой природе.)

+1

Чтение argv [0] - это решение, и ничто в этой ветке пока не убеждает меня. – bortzmeyer

+11

@bortzmeyer: рассмотрите 'execl ("/home/hacker/.hidden/malicious ","/bin/ls "," -s ", (char *) 0);' - значение 'argv [0] ' is '"/bin/ls ", но это не имеет никакого отношения к имени исполняемого файла. –

ответ

44

Функция _NSGetExecutablePath вернет полный путь к исполняемому файлу (GUI или нет). Путь может содержать символические ссылки, «..» и т. Д., Но функция realpath может использоваться для их очистки, если это необходимо. См. man3dyld для получения дополнительной информации.

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); 

Секрет этой функции состоит в том, что ядро ​​Darwin помещает путь к исполняемому файлу в стеке процесса сразу после envp массива, когда он создает процесс. Динамический редактор ссылок dyld захватывает это при инициализации и сохраняет указатель на него. Эта функция использует этот указатель.

+0

Разве не последняя строка немного сломана? – ioquatix

+0

Он возвращает 0 при успехе, или -1, если буфер недостаточно велик, и в этом случае размер 'size' заполняется требуемым размером. В этом случае вы можете выделить буфер с помощью 'malloc()'. – mark4o

+0

Итак, это эквивалентно объявлению основного с четырьмя аргументами - четвертый аргумент будет содержать защищенный argv [0]. –

2

Нет уверенного способа, я думаю. Если argv [0] является символической ссылкой, вы можете использовать readlink(). Если команда выполняется через $ PATH, то можно было бы попробовать некоторые из: поиск (GetEnv ("PATH")), GetEnv ("_"), dladdr()

+0

Это будет охватывать многие случаи, но все равно не выполняется в том случае, если вы были запущены приложением, которое игнорировалось для правильной инициализации argv [0] - что, исходя из личного опыта, относится к тревожному числу из них. –

+0

Можете ли вы привести пример такого приложения? Это не приложение, которое инициализирует argv, это libc, и приложение должно будет сделать что-то особенное для скремблирования argv [0]. – bortzmeyer

+2

Я не могу привести пример приложения с верхней части головы, который ошибочен, но все, что им нужно сделать, чтобы испортить argv [0], забудьте установить его правильно при вызове приложения через один из exec * звонки. libc будет задействован только при вызове приложения через system(). –

3

Похоже, ответ в том, что вы можете» т сделать это:

Я пытаюсь добиться чего-то вроде функциональности LSOF и собрать целую кучу статистики и информации о запущенных процессах. Если lsof были не такими медленными, я был бы счастлив придерживаться с ним.

Если вы переописать LSOF, вы найдете , что это медленно, потому что это делает много работы.

Я предполагаю, что это на самом деле не потому, что Lsof является пользовательским режимом, это более, что он должен сканирования через адресное пространство задачи по ищет вещи, подкрепленные внешнего пейджером. Есть ли более быстрый способ сделать это, когда я нахожусь в ядре ?

№ lsof не является глупым; он делает , что он должен делать. Если вы просто хотите использовать подмножество , вы можете хотите начать работать с lsof source (который доступен) и , обрезая его, чтобы удовлетворить ваши требования .

Из любопытства, является p_textvp используется на все? Похоже, что он установлен на родителя p_textvp в kern_fork (и затем получить выпустили ??), но это не получать прикоснулся ни в одном из kern_exec «s подпрограмм.

p_textvp не используется.В Дарвине proc не является корнем адреса space; задача есть. Существует нет концепция «vnode» для адресного пространства задачи, так как это не обязательно изначально заселено , отображающим один.

Если exec должен был заполнить p_textvp, то предпочтет, что все процессы поддерживаются vnode. Тогда программисты было бы предположить, что удалось получить путь к vnode, а оттуда короткий переход к предположению о том, что текущий путь к vnode является путь , из которого он был запущен, и что обработка текста на строке может привести к названию прикладного пакета ... все из которых были бы невозможны для гарантии без существенного штрафа.

Mike Smith, Darwin Drivers mailing list

+0

Я действительно очень не люблю принимать ответы, которые говорят: «Вы не можете», но эта цитата, несомненно, очень болезненно поставила гвоздь в гробу моего вопроса. –

+1

Да, я тоже ненавидел давать ответ. Я провел некоторое время на дикой охоте на гусей, пытаясь понять, могу ли я понять, как получить информацию из p_textvp, прежде чем я это обнаружил. –

0

Почему не просто realpath(argv[0], actualpath);? Правда, realpath имеет некоторые ограничения (задокументированные на странице руководства), но отлично справляется с символическими ссылками. Испытано на FreeBSD и 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); 
} 

Если программа запущена через PATH, см решение pixelbeat в.

+0

Это не удается, когда немая программа вызывает вас при вызове exec * и неправильно инициализирует структуру argv, так что argv [0] является либо просто исполняемым именем (т. Е. Не полным), либо неверным (отсутствует, нулевая строка , или что у вас есть). –

+1

Если argv [0] не полный путь, не проблема, realpath() будет обрабатывать его. Если он пуст или NULL, ну, это ошибка вызывающего абонента, а не моей программы :-) – bortzmeyer

+0

@BenjaminPollack: Нет ничего отдаленно неправильного в первом, если исполняемый файл находится в '$ PATH'! – SamB

0
+0

Он работает, если ваше приложение является графическим приложением, запущенным через Finder и связанными с Carbon. В этом случае, однако, '[[NSBundle mainBundle] bundlePath]' тоже будет работать - и не создавать «FSRef» и определять серийный номер процесса. –

+0

@BenjaminPollack '[[NSBundle mainBundle] bundlePath]' также работает для приложений, отличных от UI, которые представляют собой только один двоичный файл, если они связаны с 'Foundation'. Если ваше приложение просто связывается с 'CoreFoundation', вы можете использовать' CFBundle'. Все методы пакета также работают для простого двоичного файла, который вообще не является пакетом, хотя они могут не всегда возвращать полезную информацию, но они работают для получения исполняемого пути вашего двоичного файла. – Mecki

24

Я считаю, что гораздо более элегантное решение, которое на самом деле работает для любого PID, а также возвращает абсолютный путь непосредственно:

#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; 
} 
+1

Мне нравится ваше решение. Ален, работает как шарм. – Dave

+2

Спасибо! Одна вещь, хотя, используя OS X 10.8.5, это не сработало для меня без: #include Charlesism

+0

Лучшее решение, которое я мог бы получить. Очень чистая работа! – Pal

1

Это поздно, но [[NSBundle mainBundle] executablePath] работает просто отлично для не сгруппирована, команды лайн.

Смежные вопросы