How to handle readlink () from "/ proc / self / exe" when the executable is replaced at runtime?

In my C ++ application, my execv() application in the fork() ed child process uses the same executable to handle some work in the new child process with different arguments that communicate with the channels in the parent process. To get the name of the path to myself, I execute the following code on the Linux port (I have different code on the Macintosh):

  const size_t bufSize = PATH_MAX + 1; char dirNameBuffer[bufSize]; // Read the symbolic link '/proc/self/exe'. const char *linkName = "/proc/self/exe"; const int ret = int(readlink(linkName, dirNameBuffer, bufSize - 1)); 

However, if, at runtime, I replace the executable with an updated version of the binary on disk, the result of the readlink() : "/usr/local/bin/myExecutable (deleted)"

I understand that my executable has been replaced with a newer updated version, and the source for /proc/self/exe now replaced, however, when I go to execv() , it now fails with errno 2 - No such file or directory. to additional trailing " (deleted)" as a result.

I would like execv() use the old executable for itself or updated. I can simply identify the line ending in " (deleted)" and change it to omit this and allow the updated executable, but that seems awkward to me.

How can I execv() execute the current executable (or replace it if it's easier) with a new set of arguments when the original executable was replaced with an updated one at runtime?

+8
c ++ linux fork exec self-reference
source share
3 answers

Instead of using readlink to open the path to your own executable, you can directly call open on /proc/self/exe . Since the kernel already has open fd for the currently executing processes, this will give you fd regardless of whether the path has been replaced by a new executable or not.

Then you can use fexecve instead of execv , which takes the fd parameter instead of the filename parameter for the executable.

 int fd = open("/proc/self/exe", O_RDONLY); fexecve(fd, argv, envp); 

There is no error in the above code for brevity.

+4
source share

One solution is in an executable run (for example, near the start of main() ) to read the value of the /proc/self/exe link once and save it statically for future use:

  static string savedBinary; static bool initialized = false; // To deal with issue of long running executable having its binary replaced // with a newer one on disk, we compute the resolved binary once at startup. if (!initialized) { const size_t bufSize = PATH_MAX + 1; char dirNameBuffer[bufSize]; // Read the symbolic link '/proc/self/exe'. const char *linkName = "/proc/self/exe"; const int ret = int(readlink(linkName, dirNameBuffer, bufSize - 1)); savedBinary = dirNameBuffer; // On at least Linux, if the executable is replaced, readlink() of // "/proc/self/exe" gives "/usr/local/bin/flume (deleted)". // Therefore, we just compute the binary location statically once at // startup, before it can possibly be replaced, but we leave this code // here as an extra precaution. const string deleted(" (deleted)"); const size_t deletedSize = deleted.size(); const size_t pathSize = savedBinary.size(); if (pathSize > deletedSize) { const size_t matchPos = pathSize - deletedSize; if (0 == savedBinary.compare(matchPos, deletedSize, deleted)) { // Deleted original binary, Issue warning, throw an exception, or exit. // Or cludge the original path with: savedBinary.erase(matchPos); } } initialized = true; } // Use savedBinary value. 

Thus, it is very unlikely that the original executable will be replaced within microseconds of main (), caching the path to its binary. Thus, a long-term application (for example, hours or days) can be replaced on the disk, but on the first question, it could fork() and execv() update the binary, which may have fixed the error. This has the added benefit of being cross-platform, and therefore different Macintosh code for reading the binary path can also be protected from changing binaries after startup.

ATTENTION Editor’s Note: readlink does not end the line zero, so the above program may or may not work by accident if the buffer was not filled with zeros before calling readlink

+2
source share

The reason you get the (deleted) part in a symbolic link is because you replaced the file with the correct binary program text with another file, and the symbolic link to the executable file is again invalid. Suppose you use this symbolic link to get the symbol table of this program or to load some data embedded in it, and you change the program ... the table will be wrong, and you can even collapse your program. The executable file for the program you are executing is no longer available (you deleted it), and the program that you put in its place does not match the binary executable you are executing.

When you cancel (2) an executable program, the kernel will mark a symbolic link in /proc , so the program may

  • detects that the binary has been deleted and is no longer available.
  • allows you to collect some information about his last name (instead of removing the symbolic link from the /proc tree)

You cannot write a file that is executed by the kernel, but no one bothers you to delete this file. The file will continue to be present in the file system until you execute it, but the name is not specified (this space will be freed after the exit (2) process) The kernel does not erase its contents until the inode counter in the kernel memory reaches zero, which will happen when all uses (links) to this file are subject.

+1
source share

All Articles