Sheldon Warkentin
Sheldon Warkentin

Reputation: 1746

How to get executable path with getcwd when called from a symbolic link in C

Say I have a program, abc in Linux, with a method:

char *currentPath(){
        char *path=NULL;
        path = getcwd(path, MAXPATHLEN);
        return path;
}

When calling abc directly, this path returns the path that abc is in.

If I make a symbolic link to abc, and call the symbolic link, currentPath() returns the path of the symbolic link.

Is there a way to get make this method return the path of abc? I am interested in accessing files relative to the location of abc.

Upvotes: 0

Views: 2538

Answers (3)

TOC
TOC

Reputation: 4446

Try this code (modify it to meet your need):

#include <unistd.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>

#define MAXPATHLEN      256

char buffer[1024];

char *currentPath(char *bin_path){
        char *path = NULL;
        ssize_t len;
        struct stat file_stat;

        if (lstat(bin_path, &file_stat) != 0) 
        {
                /* handle error */
                fprintf(stderr, "ERROR\n");
        }

        if (!S_ISREG(file_stat.st_mode)) 
        {
                /* file is a symlink */
                fprintf(stdout, "Is a symlink\n");
                if ((len = readlink(bin_path, buffer, sizeof(buffer)-1)) != -1)
                        buffer[len] = '\0';
                /* In this case we return the name of the link */
                return buffer;
        }
        else
        {
                fprintf(stdout, "Is a regular file\n");
                path = getcwd(bin_path, MAXPATHLEN);

                return path;
        }
}

int main(int argc, char **argv)
{
        fprintf(stdout, "My path is : %s\n", currentPath(argv[0]));

        return 0;
}

Upvotes: 0

Geoff Reedy
Geoff Reedy

Reputation: 36061

Use realpath(const char *path, char *resolved_path)

realpath() expands all symbolic links and resolves references to /./, /../ and extra '/' characters in the null-terminated string named by path to produce a canonicalized absolute pathname.

In your case:

char *currentPath() {
  char *path, *canon_path;
  path = getcwd(NULL, MAXPATHLEN);
  canon_path = realpath(path, NULL);
  free(path);
  return canon_path;
}

Note that this does not get the path of the executable program (it is unclear what you're trying to do). To do that portably is trickier. You'll need to use the value of argv[0] to get it:

char *bindir(char *argv0) {
  char *canon_path = realpath(argv0, NULL);
  char *canon_dir = strdup(dirname(canon_path));
  free(canon_path);
  return canon_dir;
}

The strdup call is required because dirname may modify its argument and return a pointer into that or return a pointer to a statically allocated buffer.

Upvotes: 2

Jonathan Leffler
Jonathan Leffler

Reputation: 754590

You can't use getcwd() to help get the executable name; it is not a sensible way to go about finding the pathname of the executable.

On Linux, there is a symlink /proc/self/exe that gives you the name of the executable if you read it with the readlink() system call. Beware, the readlink() system call does not null terminate the value it returns (and I have no idea why not; it is singularly weird behaviour in my book, and an instant cause of bugs for the unwary).

There are a number of problems with your scheme.

  1. If the program is found via $PATH, there's no guarantee that the current directory is the same as the directory containing the executable. If you think about it, you run /bin/ls without having to be in the /bin directory.
  2. If you're worried about security, be aware that the value of argv[0] is under the control of the program launching the target program. The shell behaves nicely; other programs may be more malicious:

    #include <unistd.h>
    int main(void)
    {
        char *argv[] = { "/opt/you/bin/bogus", "300", 0 };
        execvp("sleep", argv);
        return(-1);
    }
    

    This passes the program name as /opt/you/bin/bogus even though it invokes the program sleep.

If you search the web, you will find plenty of examples of 'how to get the executable name' that assume that argv[0] is the way to go; it is not. Other platforms have other techniques for getting to the executable name. Using /proc/self/exe is not portable; neither are the other techniques.

Upvotes: 4

Related Questions