Finding the canonical path to an executable


Below is a coding example of how an executable can determine the canonical path to itself on the file system.  Compiler drivers, like cc, CC, f95 need to do this in order to exec the component executables that compile a program.  For example, the compiler front-end, an optimizer, and a linker.

% cat findself.c
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>

#ifndef MAXPATHLEN
#define MAXPATHLEN      1024
#endif

/* find_run_directory - find executable file in PATH
 * PARAMETERS:
 *      cmd     filename as typed by user
 *      cwd     where to return working directory
 *      dir     where to return program's directory
 *      run     where to return final resolution name
 * RETURNS:
 *      returns zero for success,
 *      -1 for error (with errno set properly).
 */
int
find_run_directory (char *cmd, char *cwd, char *dir, char **run)
{
    char                *s;

    if (!cmd || !*cmd || !cwd || !dir) {
        errno = EINVAL;         /* stupid arguments! */
        return -1;
    }

    if (*cwd != '/')
        if (getcwd (cwd, MAXPATHLEN - 1) == NULL )
            return -1;          /* cant get working directory */

    if (strchr (cmd, '/') != NULL) {
        if (realpath(cmd, dir) == NULL) {
            int lerrno = errno;
            if (chdir((const char *)cwd) == NULL)
                errno = lerrno;
            return -1;
        }
    } else {
#ifdef __linux__
        /* getexecname() not available on Linux */
        /* Note dir needs to be initialized with '\0' before
           being passed to readlink() which does not append
           a NULL terminator. */
        if (readlink("/proc/self/exe", dir, MAXPATHLEN) == -1) {
#else
        if (realpath(getexecname(), dir) == NULL) {
#endif
            int lerrno = errno;
            if (chdir((const char *)cwd) == NULL)
                errno = lerrno;
            return -1;
        }
    }

    s = strrchr (dir, '/');
    *s++ = 0;
    if (run)            /* user wants resolution name */
        *run = s;

    return 0;
}

char current_working_directory[MAXPATHLEN];
char run_directory[MAXPATHLEN];
char * run_exec_name = NULL;

int
main(int argc, char **argv)
{

    if ( !find_run_directory (argv[0],
                              current_working_directory,
                              run_directory, &run_exec_name) ) {
        (void)printf("argv[0] = %s\\n"
                         "cwd = %s\\n"
                         "run_dir = %s\\n"
                         "run_exec = %s\\n",
                     argv[0],
                     current_working_directory,
                     run_directory,
                     run_exec_name);
    } else {
        (void) printf("%s\\n", "Unaable to find run directory.");
    }

    exit (0);
}


% cc findself.c -O -o prod/bin/findself
% ls
bin         findself.c  prod
% ls bin
findself
% ls -laF bin
total 6
drwxr-xr-x   2 me      staff        512 Mar  5 10:37 ./
drwxr-xr-x   4 me      staff        512 Mar  5 10:37 ../
lrwxrwxrwx   1 me      staff         20 Mar  5 10:37 findself -> ../prod/bin/findself*
% ls -laF prod/bin          
total 22
drwxr-xr-x   2 me      staff        512 Mar  5 10:37 ./
drwxr-xr-x   3 me      staff        512 Mar  5 10:37 ../
-rwxr-xr-x   1 me      staff       8992 Mar  5 10:37 findself*
% bin/findself
argv[0] = bin/findself
cwd = /home/me/blog
run_dir = /home/me/blog/prod/bin
run_exec = findself
% prod/bin/findself
argv[0] = prod/bin/findself
cwd = /home/me/blog
run_dir = /home/me/blog/prod/bin
run_exec = findself
% setenv PATH /home/me/bin:${PATH}
% findself
argv[0] = findself
cwd = /home/me/blog
run_dir = /home/me/blog/prod/bin
run_exec = findself

Comments:

Post a Comment:
  • HTML Syntax: NOT allowed
About

Douglas is a principal software engineer working as the C compiler project lead and the Oracle Solaris Studio technical lead.

Search

Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today