Поиск текущего исполняемого пути без/proc/self/exe

Мне кажется, что Linux легко справляется с /proc/self/exe. Но я хотел бы знать, есть ли удобный способ найти текущий каталог приложений в C/С++ с кросс-платформенными интерфейсами. Я видел некоторые проекты, сбрасывающие с argv [0], но это не кажется полностью надежным.

Если вам когда-либо приходилось поддерживать, скажем, Mac OS X, у которой нет /proc/, что бы вы сделали? Использовать #ifdefs для выделения кода для конкретной платформы (например, NSBundle)? Или попытайтесь вывести исполняемый путь из argv [0], $PATH и еще чего, рискуя найти ошибки в крайних случаях?

Ответ 1

Некоторые интерфейсы, специфичные для ОС:

Портативный (но менее надежный) метод заключается в использовании argv[0]. Хотя он может быть настроен на что-либо вызывающей программой, по соглашению устанавливается либо имя пути исполняемого файла, либо имя, которое было найдено с помощью $PATH.

Некоторые оболочки, в том числе bash и ksh, устанавливают переменную окружения "_" на полный путь исполняемого файла до этого выполняется. В этом случае вы можете использовать getenv("_"), чтобы получить его. Однако это ненадежно, потому что не все оболочки это делают, и он может быть установлен на что-либо или оставлен после родительского процесса, который не изменил его перед выполнением вашей программы.

Ответ 2

Использование /proc/self/exe является не переносным и ненадежным. В моей системе Ubuntu 12.04 вы должны быть root, чтобы читать/следовать символической ссылке. Это приведет к примеру Boost и, вероятно, решения, связанные с whereami() не будут выполнены.

Этот пост очень длинный, но обсуждает актуальные проблемы и представляет код, который фактически работает вместе с проверкой на тестовый набор.

Лучший способ найти вашу программу - это повторить те же действия, которые использует система. Это делается с помощью argv[0] разрешенного для корневой файловой системы, pwd, среды пути и рассмотрения символических ссылок и канонизации. Это из памяти, но я сделал это в прошлом успешно и протестировал его в самых разных ситуациях. Не гарантируется работа, но если это не так, у вас, вероятно, есть гораздо большие проблемы, и она более надежна в целом, чем любые другие обсуждаемые методы. В системе, совместимой с Unix, есть ситуации, когда правильная обработка argv[0] не приведет вас к вашей программе, но затем вы выполняете ее в сертифицированной сломанной среде. Он также довольно портативен для всех производных систем Unix с 1970 года и даже некоторых не-Unix-производных систем, поскольку он в основном полагается на стандартные функции libc() и стандартную функциональность командной строки. Он должен работать на Linux (все версии), Android, Chrome OS, Minix, оригинальные Bell Labs Unix, FreeBSD, NetBSD, OpenBSD, BSD xx, SunOS, Solaris, SYSV, HPUX, Concentrix, SCO, Darwin, AIX, OS X, Nextstep и т.д. И с небольшой модификацией, вероятно, VMS, VM/CMS, DOS/Windows, ReactOS, OS/2 и т.д. Если программа запускалась непосредственно из среды GUI, она должна была установить argv[0] на абсолютную дорожка.

Поймите, что почти каждая оболочка на каждой совместимой с Unix операционной системой, которая когда-либо была выпущена, в основном находит программы одинаково и настраивает рабочую среду почти одинаково (с некоторыми дополнительными дополнительными функциями). Ожидается, что любая другая программа, запускающая программу, создаст ту же среду (argv, строки среды и т.д.) Для этой программы, как если бы она была запущена из оболочки с некоторыми необязательными дополнительными функциями. Программа или пользователь могут настроить среду, которая отличается от этого соглашения для других подчиненных программ, которые он запускает, но если это так, это ошибка, и у программы нет разумного ожидания, что подчиненная программа или ее подчиненные будут работать правильно.

Возможные значения argv[0] включают:

  • /path/to/executable - абсолютный путь
  • ../bin/executable - относительно pwd
  • bin/executable - относительно pwd
  • ./foo - относительно pwd
  • executable - basename, найти в пути
  • bin//executable - относительно pwd, неканонический
  • src/../bin/executable - относительно pwd, неканоническое, обратное отслеживание
  • bin/./echoargc - относительно pwd, неканоническое

Значения, которые вы не должны видеть:

  • ~/bin/executable - перезаписано до запуска вашей программы.
  • ~user/bin/executable - перезаписано до запуска вашей программы
  • alias - переписан до запуска вашей программы
  • $shellvariable - перезаписано до $shellvariable вашей программы
  • *foo* - подстановочный знак, переписанный до запуска вашей программы, не очень полезный
  • ?foo? - подстановочный знак, переписанный до запуска вашей программы, не очень полезный

Кроме того, они могут содержать неканонические имена путей и несколько уровней символических ссылок. В некоторых случаях может быть несколько жестких ссылок на одну и ту же программу. Например, /bin/ls, /bin/ps, /bin/chmod, /bin/rm и т.д. Могут быть жесткими ссылками на /bin/busybox.

Чтобы найти себя, выполните следующие действия:

  • Сохраните pwd, PATH и argv [0] при входе в вашу программу (или инициализации вашей библиотеки), поскольку они могут измениться позже.

  • Необязательно: особенно для систем, отличных от Unix, отдельно, но не отбрасывайте часть префикса хоста/пользователя/привода pathname, если они есть; часть, которая часто предшествует двоеточию или следует за начальным "//".

  • Если argv[0] является абсолютным путем, используйте это как отправную точку. Абсолютный путь, вероятно, начинается с "/", но в некоторых не-Unix-системах он может начинаться с "\" или префикса буквы диска или имени, за которым следует двоеточие.

  • Else, если argv[0] - относительный путь (содержит "/" или "\", но не начинается с него, например "../../bin/foo", а затем объединить pwd+ "/" [ CN00] [0] (используйте текущую рабочую директорию с момента запуска программы, а не по текущему).

  • Else, если argv [0] - это простое базовое имя (без косых черт), затем по очереди объединяйте его с каждой записью в переменной среды PATH и попробуйте те и используйте первую, которая будет успешной.

  • Необязательно: просто попробуйте использовать определенную платформу /proc/self/exe, /proc/curproc/file (BSD) и (char *)getauxval(AT_EXECFN) и dlgetname(...) если они есть. Вы даже можете попробовать их до использования методов argv[0] -based, если они доступны, и вы не сталкиваетесь с проблемами разрешения. В несколько маловероятном случае (когда вы рассматриваете все версии всех систем), что они присутствуют и не терпят неудачу, они могут быть более авторитетными.

  • Необязательно: проверьте имя пути, переданное с использованием параметра командной строки.

  • Необязательно: проверьте имя пути в среде, явно переданной вашим сценарием оболочки, если таковой имеется.

  • Необязательно: в качестве последней меры попробуйте переменную среды "_". Это может указывать на другую программу целиком, такую как оболочка пользователей.

  • Разрешить символические ссылки, может быть несколько уровней. Существует возможность бесконечных циклов, хотя, если они существуют, ваша программа, вероятно, не будет вызвана.

  • Канонизировать имя файла, разрешив подстроки типа "/foo/../bar/" на "/bar/". Обратите внимание, что это может потенциально изменить значение, если вы пересекаете точку подключения к сети, поэтому канонизация не всегда хороша. На сетевом сервере ".." в symlink может использоваться для перемещения по пути к другому файлу в контексте сервера, а не на клиенте. В этом случае вам, вероятно, нужен клиентский контекст, поэтому канонизация становится ок. Также преобразуйте шаблоны типа "/./" в "/" и "//" в "/". В оболочке readlink --canonicalize разрешает множественные символические readlink --canonicalize и имя canonicalize. Чейз может делать подобное, но не установлен. realpath() или canonicalize_file_name(), если они есть, могут помочь.

Если realpath() не существует во время компиляции, вы можете заимствовать копию из лицензированного дистрибутива библиотеки и скомпилировать ее в себе, а не изобретать колесо. Исправьте переполнение потенциального буфера (перейдите в размерный буфер вывода, подумайте, что strncpy() vs strcpy()), если вы будете использовать буфер меньше PATH_MAX. Может быть проще просто использовать переименованную частную копию, а не тестировать ее, если она существует. Разрешительная копия лицензии с android/darwin/bsd: https://android.googlesource.com/platform/bionic/+/f077784/libc/upstream-freebsd/lib/libc/stdlib/realpath.c

Имейте в виду, что несколько попыток могут быть успешными или частично успешными, и они могут не указывать на один и тот же исполняемый файл, поэтому попробуйте проверить свой исполняемый файл; однако у вас может не быть разрешения на чтение - если вы не можете его прочитать, не рассматривайте это как отказ. Или проверьте что-то рядом с вашим исполняемым файлом, например, каталог "../lib/", который вы пытаетесь найти. У вас может быть несколько версий, упакованные и локально скомпилированные версии, локальные и сетевые версии, а также портативные версии локальных и USB-накопителей и т.д., И существует небольшая вероятность того, что вы можете получить два несовместимых результата из разных методов поиска. И "_" может просто указывать на неправильную программу.

Программа, использующая execve может преднамеренно установить argv[0] как несовместимую с фактическим путем, используемым для загрузки программы, и коррумпированными PATH, "_", pwd и т.д., Хотя обычно нет оснований для этого; но это может иметь последствия для безопасности, если у вас есть уязвимый код, который игнорирует тот факт, что среда выполнения может быть изменена различными способами, включая, но не ограничиваясь этим, (chroot, fuse файловая система, жесткие ссылки и т.д.) Возможно для команд оболочки для установки PATH, но не экспортировать их.

Вам не обязательно нужно кодировать системы, отличные от Unix, но было бы неплохо знать некоторые особенности, чтобы вы могли написать код таким образом, что кому-то не так сложно переносить позже, Имейте в виду, что некоторые системы (DEC VMS, DOS, URL и т.д.) Могут иметь имена дисков или другие префиксы, которые заканчиваются двоеточием, таким как "C: \", "sys $ drive: [foo] bar" и "file :///Foo/бар/Баз". В старых системах DEC VMS используются "[" и "]", чтобы заключить часть каталога в путь, хотя это может быть изменено, если ваша программа скомпилирована в среде POSIX. Некоторые системы, такие как VMS, могут иметь версию файла (разделенные точкой с запятой в конце). Некоторые системы используют две последовательные косые черты, как в "//drive/path/to/file" или "user @host: /path/to/file" (команда scp) или "file://hostname/path/to/file", (URL). В некоторых случаях (DOS, windoze) PATH может иметь разные разделительные символы - ";" vs ":" и "\" против "/" для разделителя путей. В csh/tsh есть "путь" (ограниченный пробелами) и "PATH", разделенный двоеточиями, но ваша программа должна получать PATH, поэтому вам не нужно беспокоиться о пути. DOS и некоторые другие системы могут иметь относительные пути, начинающиеся с префикса диска. C: foo.exe относится к foo.exe в текущем каталоге на диске C, поэтому вам нужно искать текущий каталог на C: и использовать его для pwd.

Пример символических ссылок и оберток в моей системе:

/usr/bin/google-chrome is symlink to
/etc/alternatives/google-chrome  which is symlink to
/usr/bin/google-chrome-stable which is symlink to
/opt/google/chrome/google-chrome which is a bash script which runs
/opt/google/chome/chrome

Обратите внимание, что пользовательский счет отправил ссылку выше на программу в HP, которая обрабатывает три основных случая argv[0]. Однако для этого нужны некоторые изменения:

  • Необходимо будет переписать все strcat() и strcpy() для использования strncat() и strncpy(). Несмотря на то, что переменные объявлены длиной PATHMAX, входное значение длины PATHMAX-1 плюс длина конкатенированных строк> PATHMAX, а входное значение длины PATHMAX будет прекращено.
  • Его нужно переписать как библиотечную функцию, а не просто распечатывать результаты.
    • Он не может канонизировать имена (используйте код realpath, с которым я связан выше)
    • Он не может разрешить символические ссылки (используйте код realpath)

Итак, если вы объедините как код HP, так и код реального пути и исправьте оба, чтобы быть стойкими к переполнениям буфера, тогда у вас должно быть что-то, что может правильно интерпретировать argv[0].

Ниже показаны фактические значения argv[0] для различных способов вызова одной и той же программы на Ubuntu 12.04. И да, программа была случайно названа echoargc вместо echoargv. Это было сделано с использованием сценария для чистого копирования, но его выполнение вручную в оболочке получает одинаковые результаты (кроме псевдонимов не работает в скрипте, если вы явно не разрешаете их).

cat ~/src/echoargc.c
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
main(int argc, char **argv)
{
  printf("  argv[0]=\"%s\"\n", argv[0]);
  sleep(1);  /* in case run from desktop */
}
tcc -o ~/bin/echoargc ~/src/echoargc.c 
cd ~
/home/whitis/bin/echoargc
  argv[0]="/home/whitis/bin/echoargc"
echoargc
  argv[0]="echoargc"
bin/echoargc
  argv[0]="bin/echoargc"
bin//echoargc
  argv[0]="bin//echoargc"
bin/./echoargc
  argv[0]="bin/./echoargc"
src/../bin/echoargc
  argv[0]="src/../bin/echoargc"
cd ~/bin
*echo*
  argv[0]="echoargc"
e?hoargc
  argv[0]="echoargc"
./echoargc
  argv[0]="./echoargc"
cd ~/src
../bin/echoargc
  argv[0]="../bin/echoargc"
cd ~/junk
~/bin/echoargc
  argv[0]="/home/whitis/bin/echoargc"
~whitis/bin/echoargc
  argv[0]="/home/whitis/bin/echoargc"
alias echoit=~/bin/echoargc
echoit
  argv[0]="/home/whitis/bin/echoargc"
echoarg=~/bin/echoargc
$echoarg
  argv[0]="/home/whitis/bin/echoargc"
ln -s ~/bin/echoargc junk1
./junk1
  argv[0]="./junk1"
ln -s /home/whitis/bin/echoargc junk2
./junk2
  argv[0]="./junk2"
ln -s junk1 junk3
./junk3
  argv[0]="./junk3"


gnome-desktop-item-edit --create-new ~/Desktop
# interactive, create desktop link, then click on it
  argv[0]="/home/whitis/bin/echoargc"
# interactive, right click on gnome application menu, pick edit menus
# add menu item for echoargc, then run it from gnome menu
 argv[0]="/home/whitis/bin/echoargc"

 cat ./testargcscript 2>&1 | sed -e 's/^/    /g'
#!/bin/bash
# echoargc is in ~/bin/echoargc
# bin is in path
shopt -s expand_aliases
set -v
cat ~/src/echoargc.c
tcc -o ~/bin/echoargc ~/src/echoargc.c 
cd ~
/home/whitis/bin/echoargc
echoargc
bin/echoargc
bin//echoargc
bin/./echoargc
src/../bin/echoargc
cd ~/bin
*echo*
e?hoargc
./echoargc
cd ~/src
../bin/echoargc
cd ~/junk
~/bin/echoargc
~whitis/bin/echoargc
alias echoit=~/bin/echoargc
echoit
echoarg=~/bin/echoargc
$echoarg
ln -s ~/bin/echoargc junk1
./junk1
ln -s /home/whitis/bin/echoargc junk2
./junk2
ln -s junk1 junk3
./junk3

Эти примеры иллюстрируют, что методы, описанные в этом сообщении, должны работать в самых разных условиях и почему некоторые из этапов необходимы.

EDIT: теперь программа, которая печатает argv [0], была обновлена, чтобы на самом деле найти себя.

// Copyright 2015 by Mark Whitis.  License=MIT style
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <limits.h>
#include <assert.h>
#include <string.h>
#include <errno.h>

// "look deep into yourself, Clarice"  -- Hanibal Lector
char findyourself_save_pwd[PATH_MAX];
char findyourself_save_argv0[PATH_MAX];
char findyourself_save_path[PATH_MAX];
char findyourself_path_separator='/';
char findyourself_path_separator_as_string[2]="/";
char findyourself_path_list_separator[8]=":";  // could be ":; "
char findyourself_debug=0;

int findyourself_initialized=0;

void findyourself_init(char *argv0)
{

  getcwd(findyourself_save_pwd, sizeof(findyourself_save_pwd));

  strncpy(findyourself_save_argv0, argv0, sizeof(findyourself_save_argv0));
  findyourself_save_argv0[sizeof(findyourself_save_argv0)-1]=0;

  strncpy(findyourself_save_path, getenv("PATH"), sizeof(findyourself_save_path));
  findyourself_save_path[sizeof(findyourself_save_path)-1]=0;
  findyourself_initialized=1;
}


int find_yourself(char *result, size_t size_of_result)
{
  char newpath[PATH_MAX+256];
  char newpath2[PATH_MAX+256];

  assert(findyourself_initialized);
  result[0]=0;

  if(findyourself_save_argv0[0]==findyourself_path_separator) {
    if(findyourself_debug) printf("  absolute path\n");
     realpath(findyourself_save_argv0, newpath);
     if(findyourself_debug) printf("  newpath=\"%s\"\n", newpath);
     if(!access(newpath, F_OK)) {
        strncpy(result, newpath, size_of_result);
        result[size_of_result-1]=0;
        return(0);
     } else {
    perror("access failed 1");
      }
  } else if( strchr(findyourself_save_argv0, findyourself_path_separator )) {
    if(findyourself_debug) printf("  relative path to pwd\n");
    strncpy(newpath2, findyourself_save_pwd, sizeof(newpath2));
    newpath2[sizeof(newpath2)-1]=0;
    strncat(newpath2, findyourself_path_separator_as_string, sizeof(newpath2));
    newpath2[sizeof(newpath2)-1]=0;
    strncat(newpath2, findyourself_save_argv0, sizeof(newpath2));
    newpath2[sizeof(newpath2)-1]=0;
    realpath(newpath2, newpath);
    if(findyourself_debug) printf("  newpath=\"%s\"\n", newpath);
    if(!access(newpath, F_OK)) {
        strncpy(result, newpath, size_of_result);
        result[size_of_result-1]=0;
        return(0);
     } else {
    perror("access failed 2");
      }
  } else {
    if(findyourself_debug) printf("  searching $PATH\n");
    char *saveptr;
    char *pathitem;
    for(pathitem=strtok_r(findyourself_save_path, findyourself_path_list_separator,  &saveptr); pathitem; pathitem=strtok_r(NULL, findyourself_path_list_separator, &saveptr) ) {
       if(findyourself_debug>=2) printf("pathitem=\"%s\"\n", pathitem);
       strncpy(newpath2, pathitem, sizeof(newpath2));
       newpath2[sizeof(newpath2)-1]=0;
       strncat(newpath2, findyourself_path_separator_as_string, sizeof(newpath2));
       newpath2[sizeof(newpath2)-1]=0;
       strncat(newpath2, findyourself_save_argv0, sizeof(newpath2));
       newpath2[sizeof(newpath2)-1]=0;
       realpath(newpath2, newpath);
       if(findyourself_debug) printf("  newpath=\"%s\"\n", newpath);
      if(!access(newpath, F_OK)) {
          strncpy(result, newpath, size_of_result);
          result[size_of_result-1]=0;
          return(0);
      } 
    } // end for
    perror("access failed 3");

  } // end else
  // if we get here, we have tried all three methods on argv[0] and still haven't succeeded.   Include fallback methods here.
  return(1);
}

main(int argc, char **argv)
{
  findyourself_init(argv[0]);

  char newpath[PATH_MAX];
  printf("  argv[0]=\"%s\"\n", argv[0]);
  realpath(argv[0], newpath);
  if(strcmp(argv[0],newpath)) { printf("  realpath=\"%s\"\n", newpath); }
  find_yourself(newpath, sizeof(newpath));
  if(1 || strcmp(argv[0],newpath)) { printf("  findyourself=\"%s\"\n", newpath); }
  sleep(1);  /* in case run from desktop */
}

И вот результат, который показывает, что в каждом из предыдущих тестов он действительно нашел себя.

tcc -o ~/bin/echoargc ~/src/echoargc.c 
cd ~
/home/whitis/bin/echoargc
  argv[0]="/home/whitis/bin/echoargc"
  findyourself="/home/whitis/bin/echoargc"
echoargc
  argv[0]="echoargc"
  realpath="/home/whitis/echoargc"
  findyourself="/home/whitis/bin/echoargc"
bin/echoargc
  argv[0]="bin/echoargc"
  realpath="/home/whitis/bin/echoargc"
  findyourself="/home/whitis/bin/echoargc"
bin//echoargc
  argv[0]="bin//echoargc"
  realpath="/home/whitis/bin/echoargc"
  findyourself="/home/whitis/bin/echoargc"
bin/./echoargc
  argv[0]="bin/./echoargc"
  realpath="/home/whitis/bin/echoargc"
  findyourself="/home/whitis/bin/echoargc"
src/../bin/echoargc
  argv[0]="src/../bin/echoargc"
  realpath="/home/whitis/bin/echoargc"
  findyourself="/home/whitis/bin/echoargc"
cd ~/bin
*echo*
  argv[0]="echoargc"
  realpath="/home/whitis/bin/echoargc"
  findyourself="/home/whitis/bin/echoargc"
e?hoargc
  argv[0]="echoargc"
  realpath="/home/whitis/bin/echoargc"
  findyourself="/home/whitis/bin/echoargc"
./echoargc
  argv[0]="./echoargc"
  realpath="/home/whitis/bin/echoargc"
  findyourself="/home/whitis/bin/echoargc"
cd ~/src
../bin/echoargc
  argv[0]="../bin/echoargc"
  realpath="/home/whitis/bin/echoargc"
  findyourself="/home/whitis/bin/echoargc"
cd ~/junk
~/bin/echoargc
  argv[0]="/home/whitis/bin/echoargc"
  findyourself="/home/whitis/bin/echoargc"
~whitis/bin/echoargc
  argv[0]="/home/whitis/bin/echoargc"
  findyourself="/home/whitis/bin/echoargc"
alias echoit=~/bin/echoargc
echoit
  argv[0]="/home/whitis/bin/echoargc"
  findyourself="/home/whitis/bin/echoargc"
echoarg=~/bin/echoargc
$echoarg
  argv[0]="/home/whitis/bin/echoargc"
  findyourself="/home/whitis/bin/echoargc"
rm junk1 junk2 junk3
ln -s ~/bin/echoargc junk1
./junk1
  argv[0]="./junk1"
  realpath="/home/whitis/bin/echoargc"
  findyourself="/home/whitis/bin/echoargc"
ln -s /home/whitis/bin/echoargc junk2
./junk2
  argv[0]="./junk2"
  realpath="/home/whitis/bin/echoargc"
  findyourself="/home/whitis/bin/echoargc"
ln -s junk1 junk3
./junk3
  argv[0]="./junk3"
  realpath="/home/whitis/bin/echoargc"
  findyourself="/home/whitis/bin/echoargc"

Два описанных выше GUI-запуска также правильно найдут программу.

Существует одна потенциальная ловушка. Функция access() отбрасывает разрешения, если программа установлена перед тестированием. Если есть ситуация, когда программа может быть найдена как пользователь с повышенными правами, но не как обычный пользователь, тогда может возникнуть ситуация, когда эти тесты потерпят неудачу, хотя маловероятно, что программа действительно может быть выполнена в этих обстоятельствах. Вместо этого можно использовать euidaccess(). Возможно, однако, что он может найти недоступную программу ранее на пути, чем может сделать пользователь.

Ответ 3

Посмотрите библиотеку whereami от Gregory Pakosz (у которой есть только один файл C); он позволяет вам получить полный путь к текущему исполняемому файлу на различных платформах. В настоящее время он доступен как репо на github here.

Ответ 4

Альтернативой Linux для использования либо /proc/self/exe, либо argv[0] является использование информации, переданной интерпретатором ELF, предоставляемой glibc как таковой:

#include <stdio.h>
#include <sys/auxv.h>

int main(int argc, char **argv)
{
    printf("%s\n", (char *)getauxval(AT_EXECFN));
    return(0);
}

Обратите внимание, что getauxval является расширением glibc и должно быть надежным, вы должны проверить, чтобы он не возвращал NULL (что указывает, что интерпретатор ELF не предоставил параметр AT_EXECFN), но я не Не думаю, что это действительно проблема Linux.

Ответ 5

Если вам когда-либо приходилось поддерживать, скажем, Mac OS X, у которой нет /proc/, что вы бы сделали? Используйте #ifdefs для изолировать код, специфичный для платформы (Например, NSBundle)?

Да, специальный код изолирующей платформы с #ifdefs является обычным способом.

Другой подход состоял бы в том, чтобы иметь чистый #ifdef безвестный заголовок, который содержит объявления функций и помещает реализации в файлы исходных файлов, специфичных для платформы. Например, посмотрите, как библиотека Poco С++ делает что-то подобное для своего класса Environment.

Ответ 6

Для надежной работы этой платформы на разных платформах необходимо использовать инструкции #ifdef.

Нижеприведенный код находит исполняемый путь в Windows, Linux, MacOS, Solaris или FreeBSD (хотя FreeBSD не проверен). Он использует boost >= 1.55.0 для упрощения кода, но его достаточно легко удалить, если хотите. Просто используйте такие параметры, как _MSC_VER и __linux, как требуется ОС и компилятору.

#include <string>
#include <boost/predef/os.h>

#if (BOOST_OS_WINDOWS)
#  include <stdlib.h>
#elif (BOOST_OS_SOLARIS)
#  include <stdlib.h>
#  include <limits.h>
#elif (BOOST_OS_LINUX)
#  include <unistd.h>
#  include <limits.h>
#elif (BOOST_OS_MACOS)
#  include <mach-o/dyld.h>
#elif (BOOST_OS_BSD_FREE)
#  include <sys/types.h>
#  include <sys/sysctl.h>
#endif

/*
 * Returns the full path to the currently running executable,
 * or an empty string in case of failure.
 */
std::string getExecutablePath() {
#if (BOOST_OS_WINDOWS)
    char *exePath;
    if (_get_pgmptr(&exePath) != 0)
        exePath = "";
#elif (BOOST_OS_SOLARIS)
    char exePath[PATH_MAX];
    if (realpath(getexecname(), exePath) == NULL)
        exePath[0] = '\0';
#elif (BOOST_OS_LINUX)
    char exePath[PATH_MAX];
    ssize_t len = ::readlink("/proc/self/exe", exePath, sizeof(exePath));
    if (len == -1 || len == sizeof(exePath))
        len = 0;
    exePath[len] = '\0';
#elif (BOOST_OS_MACOS)
    char exePath[PATH_MAX];
    uint32_t len = sizeof(exePath);
    if (_NSGetExecutablePath(exePath, &len) != 0) {
        exePath[0] = '\0'; // buffer too small (!)
    } else {
        // resolve symlinks, ., .. if possible
        char *canonicalPath = realpath(exePath, NULL);
        if (canonicalPath != NULL) {
            strncpy(exePath,canonicalPath,len);
            free(canonicalPath);
        }
    }
#elif (BOOST_OS_BSD_FREE)
    char exePath[2048];
    int mib[4];  mib[0] = CTL_KERN;  mib[1] = KERN_PROC;  mib[2] = KERN_PROC_PATHNAME;  mib[3] = -1;
    size_t len = sizeof(exePath);
    if (sysctl(mib, 4, exePath, &len, NULL, 0) != 0)
        exePath[0] = '\0';
#endif
    return std::string(exePath);
}

Вышеприведенная версия возвращает полные пути, включая имя исполняемого файла. Если вместо этого вы хотите использовать путь без имени исполняемого файла, #include boost/filesystem.hpp> и изменить оператор return на:

return strlen(exePath)>0 ? boost::filesystem::path(exePath).remove_filename().make_preferred().string() : std::string();

Ответ 8

В зависимости от версии QNX Neutrino существуют разные способы найти полный путь и имя исполняемого файла, который использовался для запуска запущенного процесса. Я обозначаю идентификатор процесса как <PID>. Попробуйте следующее:

  • Если файл /proc/self/exefile существует, то его содержимое является запрашиваемой информацией.
  • Если файл /proc/<PID>/exefile существует, то его содержимое является запрашиваемой информацией.
  • Если файл /proc/self/as существует, то:
    • open() файл.
    • Выделите буфер, по крайней мере, sizeof(procfs_debuginfo) + _POSIX_PATH_MAX.
    • Дайте этот буфер в качестве входного сигнала для devctl(fd, DCMD_PROC_MAPDEBUG_BASE,....
    • Передача буфера в procfs_debuginfo*.
    • Запрошенная информация находится в поле path структуры procfs_debuginfo. Предупреждение. По какой-то причине иногда QNX пропускает первую косую черту / пути к файлу. При необходимости подготовьте /.
    • Очистите (закройте файл, освободите буфер и т.д.).
  • Попробуйте выполнить процедуру в 3. с файлом /proc/<PID>/as.
  • Попробуйте dladdr(dlsym(RTLD_DEFAULT, "main"), &dlinfo) где dlinfo - это структура Dl_info, чья dli_fname может содержать запрошенную информацию.

Надеюсь, это поможет.

Ответ 9

AFAIK, нет такого пути. И есть также неоднозначность: что бы вы хотели получить в качестве ответа, если один и тот же исполняемый файл имеет несколько жестких ссылок, "указывающих" на него? (Жесткие ссылки на самом деле не "указывают", они являются одним и тем же файлом, просто в другом месте в иерархии FS.) После того, как execve() успешно выполняет новый двоичный код, вся информация о его аргументах теряется.

Ответ 10

Более портативный способ получить имя пути исполняемого изображения:

ps может предоставить вам путь к исполняемому файлу, если у вас есть идентификатор процесса. Также ps - это утилита POSIX, поэтому она должна быть переносимой

поэтому, если идентификатор процесса равен 249297, тогда эта команда дает вам только имя пути.

    ps -p 24297 -o comm --no-heading

Объяснение аргументов

-p - выбирает данный процесс

-o comm - отображает имя команды (-o cmd выбирает всю командную строку)

- no-heading - не отображает строку заголовка, только вывод.

Программа C может запускать это через popen.

Ответ 11

Если вы используете C, вы можете использовать функцию getwd:

int main()
{       
 char buf[4096];
 getwd(buf);
 printf(buf);
}

Это вы будете печатать на стандартном выходе, в текущем каталоге исполняемого файла.

Ответ 12

Путь абсолютного значения программы находится в PWD envp вашей основной функции, также есть функция в C, называемая getenv, поэтому там.