How to implement readlink to find the path

42,077

Solution 1

This Use the readlink() function properly for the correct uses of the readlink function.

If you have your path in a std::string, you could do something like this:

#include <unistd.h>
#include <limits.h>

std::string do_readlink(std::string const& path) {
    char buff[PATH_MAX];
    ssize_t len = ::readlink(path.c_str(), buff, sizeof(buff)-1);
    if (len != -1) {
      buff[len] = '\0';
      return std::string(buff);
    }
    /* handle error condition */
}

If you're only after a fixed path:

std::string get_selfpath() {
    char buff[PATH_MAX];
    ssize_t len = ::readlink("/proc/self/exe", buff, sizeof(buff)-1);
    if (len != -1) {
      buff[len] = '\0';
      return std::string(buff);
    }
    /* handle error condition */
}

To use it:

int main()
{
  std::string selfpath = get_selfpath();
  std::cout << selfpath << std::endl;
  return 0;
}

Solution 2

Accepted answer is almost correct, except you can't rely on PATH_MAX because it is

not guaranteed to be defined per POSIX if the system does not have such limit.

(From readlink(2) manpage)

Also, when it's defined it doesn't always represent the "true" limit. (See http://insanecoding.blogspot.fr/2007/11/pathmax-simply-isnt.html )

The readlink's manpage also give a way to do that on symlink :

Using a statically sized buffer might not provide enough room for the symbolic link contents. The required size for the buffer can be obtained from the stat.st_size value returned by a call to lstat(2) on the link. However, the number of bytes written by readlink() and read‐ linkat() should be checked to make sure that the size of the symbolic link did not increase between the calls.

However in the case of /proc/self/exe/ as for most of /proc files, stat.st_size would be 0. The only remaining solution I see is to resize buffer while it doesn't fit.

I suggest the use of vector<char> as follow for this purpose:

std::string get_selfpath()
{
    std::vector<char> buf(400);
    ssize_t len;

    do
    {
        buf.resize(buf.size() + 100);
        len = ::readlink("/proc/self/exe", &(buf[0]), buf.size());
    } while (buf.size() == len);

    if (len > 0)
    {
        buf[len] = '\0';
        return (std::string(&(buf[0])));
    }
    /* handle error */
    return "";
}

Solution 3

Let's look at what the manpage says:

 readlink() places the contents of the symbolic link path in the buffer
 buf, which has size bufsiz.  readlink does not append a NUL character to
 buf.

OK. Should be simple enough. Given your buffer of 1024 chars:

 char buf[1024];

 /* The manpage says it won't null terminate.  Let's zero the buffer. */
 memset(buf, 0, sizeof(buf));

 /* Note we use sizeof(buf)-1 since we may need an extra char for NUL. */
 if (readlink("/proc/self/exe", buf, sizeof(buf)-1) < 0)
 {
    /* There was an error...  Perhaps the path does not exist
     * or the buffer is not big enough.  errno has the details. */
    perror("readlink");
    return -1;
 }

Solution 4

char *
readlink_malloc (const char *filename)
{
  int size = 100;
  char *buffer = NULL;

  while (1)
    {
      buffer = (char *) xrealloc (buffer, size);
      int nchars = readlink (filename, buffer, size);
      if (nchars < 0)
        {
          free (buffer);
          return NULL;
        }
      if (nchars < size)
        return buffer;
      size *= 2;
    }
}

Taken from: http://www.delorie.com/gnu/docs/glibc/libc_279.html

Solution 5

#include <stdlib.h>
#include <unistd.h>

static char *exename(void)
{
    char *buf;
    char *newbuf;
    size_t cap;
    ssize_t len;

    buf = NULL;
    for (cap = 64; cap <= 16384; cap *= 2) {
        newbuf = realloc(buf, cap);
        if (newbuf == NULL) {
            break;
        }
        buf = newbuf;
        len = readlink("/proc/self/exe", buf, cap);
        if (len < 0) {
            break;
        }
        if ((size_t)len < cap) {
            buf[len] = 0;
            return buf;
        }
    }
    free(buf);
    return NULL;
}

#include <stdio.h>

int main(void)
{
    char *e = exename();
    printf("%s\n", e ? e : "unknown");
    free(e);
    return 0;
}

This uses the traditional "when you don't know the right buffer size, reallocate increasing powers of two" trick. We assume that allocating less than 64 bytes for a pathname is not worth the effort. We also assume that an executable pathname as long as 16384 (2**14) bytes has to indicate some kind of anomaly in how the program was installed, and it's not useful to know the pathname as we'll soon encounter bigger problems to worry about.

There is no need to bother with constants like PATH_MAX. Reserving so much memory is overkill for almost all pathnames, and as noted in another answer, it's not guaranteed to be the actual upper limit anyway. For this application, we can pick a common-sense upper limit such as 16384. Even for applications with no common-sense upper limit, reallocating increasing powers of two is a good approach. You only need log n calls for a n-byte result, and the amount of memory capacity you waste is proportional to the length of the result. It also avoids race conditions where the length of the string changes between the realloc() and the readlink().

Share:
42,077

Related videos on Youtube

a sandwhich
Author by

a sandwhich

Updated on July 09, 2022

Comments

  • a sandwhich
    a sandwhich almost 2 years

    Using the readlink function used as a solution to How do I find the location of the executable in C?, how would I get the path into a char array? Also, what do the variables buf and bufsize represent and how do I initialize them?

    EDIT: I am trying to get the path of the currently running program, just like the question linked above. The answer to that question said to use readlink("proc/self/exe"). I do not know how to implement that into my program. I tried:

    char buf[1024];  
    string var = readlink("/proc/self/exe", buf, bufsize);  
    

    This is obviously incorrect.

  • a sandwhich
    a sandwhich about 13 years
    No, sorry, I guess I didn't phrase my sentence correctly. I don't have the path, I am using readlink("/proc/self/exe", buf, bufsize); correctly in order to retrieve it.
  • Mat
    Mat about 13 years
    I don't understand what you're saying. Please edit your question to show what you have, and an example of what you want.
  • Mat
    Mat about 13 years
    ok, I put more details in, but my original answer worked quite well with a fixed path...
  • merosss
    merosss almost 10 years
    Just to add some details on which I've been struggling a bit: the get_selfPath() function returns the path including the executable name. To get just the path removing the exe name you can do the following: std::string::size_type t = path.find_last_of("/") and then path = path.substr(0,t). I don't know why everywhere this is never clarified enough;)
  • Lucio M. Tato
    Lucio M. Tato almost 10 years
    shouldn't be... if (readlink("/proc/self/exe", buf, sizeof(buf)-1) <0) ?
  • asveikau
    asveikau almost 10 years
    if (readlink(/*...*/)) tests for nonzero. Less than 0 is nonzero.
  • Lucio M. Tato
    Lucio M. Tato over 9 years
    readlink returns >0 on success. "On success, readlink() returns the number of bytes placed in buf. On error, -1 is returned". linux.die.net/man/2/readlink.
  • asveikau
    asveikau over 9 years
    Ok. Will edit then. This is a bit unusual for a syscall. Usually 0 is success.
  • Mat
    Mat over 9 years
    @a.lasram: that's not a typo at all.
  • Jackzz
    Jackzz about 9 years
    @Mat : what should be the value of the argument of the function do_readlink?
  • einpoklum
    einpoklum over 7 years
    I don't think it's a good idea to be so cavalier about allocating 4KiB on the stack like that. Why not an std::vector?
  • Ryan Burn
    Ryan Burn over 5 years
    You don't need to add mess about with adding a null terminator. Just write if (len != -1) { return std::string(buff, static_cast<size_t>(len)); }
  • yyny
    yyny about 4 years
    @einpoklum Because readlink is a low-level sys-call that doesn't have access to std::vector or even malloc. If you're really concerned about stack space, you could just put the buffer in static storage instead: static thread_local char buff[PATH_MAX];.
  • c-x-berger
    c-x-berger about 3 years
    Is there some reason for always adding 100 (rather than something like len - buf.size()?)