Recuperación mediante progtwigción de la ruta absoluta de una aplicación de línea de comandos de OS X

En Linux, una aplicación puede obtener fácilmente su ruta absoluta al consultar /proc/self/exe . En FreeBSD, es más complicado, ya que tienes que crear una llamada a sysctl:

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

pero todavía es completamente factible. Sin embargo, no puedo encontrar una forma de determinar esto en OS X para una aplicación de línea de comandos. Si está ejecutando desde un paquete de aplicaciones, puede determinarlo ejecutando [[NSBundle mainBundle] bundlePath] , pero debido a que las aplicaciones de línea de comandos no están en paquetes, esto no ayuda.

(Nota: consultar argv[0] no es una respuesta razonable, ya que, si se lanza desde un enlace simbólico, argv[0] será ese enlace simbólico, no la ruta final al ejecutable llamado. argv[0] también puede encontrarse si la aplicación tonta usa una llamada a exec() y olvida inicializar argv correctamente, que he visto en la naturaleza.)

La función _NSGetExecutablePath devolverá una ruta completa al ejecutable (GUI o no). La ruta puede contener enlaces simbólicos, ” .. “, etc., pero la función realpath se puede usar para limpiarlos si es necesario. Ver man 3 dyld para más información.

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

El secreto de esta función es que el núcleo Darwin coloca la ruta ejecutable en la stack de proceso inmediatamente después de la matriz envp cuando crea el proceso. El editor de enlaces dynamics dyld toma esto en la inicialización y mantiene un puntero al mismo. Esta función usa ese puntero.

Creo que hay una solución mucho más elegante, que realmente funciona para cualquier PID, y también devuelve la ruta absoluta directamente:

 #include  #include  #include  #include  #include  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; } 

Parece que la respuesta es que no puedes hacerlo:

Intento lograr algo así como la funcionalidad de lsof y reunir un montón de estadísticas e información sobre los procesos en ejecución. Si lsof no fuera tan lento, me encantaría seguir con eso.

Si vuelve a implementar lsof, encontrará que es lento porque está haciendo mucho trabajo.

Supongo que no es así porque lsof es el modo de usuario, es más que tiene que escanear el espacio de direcciones de una tarea buscando cosas respaldadas por un buscapersonas externo. ¿Hay alguna forma más rápida de hacerlo cuando estoy en el kernel?

No. lsof no es estúpido; está haciendo lo que tiene que hacer. Si solo desea un subconjunto de su funcionalidad, puede considerar comenzar con la fuente lsof (que está disponible) y recortarla para cumplir con sus requisitos.

Por curiosidad, ¿se usa p_textvp ? Parece que está configurado para p_textvp del p_textvp en kern_fork (y luego se libera ??) pero no se toca en ninguna de las rutinas de kern_exec .

p_textvp no se usa. En Darwin, el proceso no es la raíz del espacio de direcciones; la tarea es No existe el concepto de “vnode” para el espacio de direcciones de una tarea, ya que no necesariamente se rellena inicialmente asignando uno.

Si el ejecutivo llenase p_textvp, se verificaría la suposición de que todos los procesos están respaldados por un vnode. Entonces los progtwigdores supondrían que era posible obtener una ruta al vnode, y de ahí es un salto corto a la suposición de que la ruta actual al vnode es la ruta desde la que se lanzó, y que el procesamiento de texto en la cadena podría llevar al nombre del paquete de la aplicación … todo lo cual sería imposible de garantizar sin una penalización sustancial.

– Mike Smith, lista de correo de Darwin Drivers

No hay una manera garantizada, creo. Si argv [0] es un enlace simbólico, entonces podría usar readlink (). Si el comando se ejecuta a través de $ PATH, entonces se podría intentar: search (getenv (“PATH”)), getenv (“_”), dladdr ()

Esto es tarde, pero [[NSBundle mainBundle] executablePath] funciona muy bien para los progtwigs de línea de comandos no incluidos.

¿Por qué no simplemente realpath(argv[0], actualpath); ? Es cierto que realpath tiene algunos límites (documentados en la página del manual), pero maneja bien los enlaces simbólicos. Probado en FreeBSD y Linux

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

     % ./foobar 
     Mi camino real: / tmp / get-real-name-exe
 #include  #include  #include  #include  #include  #include  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); } 

Si el progtwig se inicia a través de PATH, consulte la solución de pixelbeat.