C - Lstat on / proc / pid / exe

时间:2014-07-05 00:22:11

标签: c symlink system-calls opensuse

我试图用lstat获取/ proc / pid / exe文件的大小(以字节为单位)。这是我的代码:

int     main(int argc, char *argv[]) 
{   
 struct stat    sb;
 char       *linkname;
 ssize_t    r;

  if (argc != 2) 
  {
    fprintf(stderr, "Usage: %s <pathname>\n", argv[0]);
    exit(EXIT_FAILURE);
  }

  if (lstat(argv[1], &sb) == -1) 
  {
    perror("lstat");
    exit(EXIT_FAILURE);
  }

  printf("sb.st_size %d\n", sb.st_size);   

  exit(EXIT_SUCCESS);
}

似乎sb.st_size总是等于0,我不明白为什么。另外,此示例是从readlink(2)手册页中提取的。

编辑:我试图让它在openSUSE上运行。

提前感谢您的帮助。

3 个答案:

答案 0 :(得分:6)

/proc中的文件不是普通文件。对于他们中的大多数人来说,stat()等。返回.st_size == 0

特别是,/proc/PID/exe实际上不是符号链接或硬链接,而是一个特殊的伪文件,其行为主要是,如符号链接。

(如果需要,您可以检测检查.st_dev字段的procfs文件。例如,与从.st_dev获得的lstat("/proc/self/exe",..)进行比较。)

要根据其PID获取特定execubtable的路径,我建议采用依赖于readlink()的返回值的方法:

#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>

/* Creative Commons CC0: Public Domain dedication
 * (In jurisdictions without public domain, this example program
 *  is licensed under the Creative Commons CC0 license.)
 *
 * To the extent possible under law, Nominal Animal has waived all
 * copyright and related or neighboring rights to this example program.
 *
 * In other words, you are free to use it in any way you wish,
 * but if it breaks something, you get to keep all the pieces.
*/

/** exe_of() - Obtain the executable path a process is running
 * @pid: Process ID
 * @sizeptr: If specified, the allocated size is saved here
 * @lenptr: If specified, the path length is saved here
 * Returns the dynamically allocated pointer to the path,
 * or NULL with errno set if an error occurs.
*/
char *exe_of(const pid_t pid, size_t *const sizeptr, size_t *const lenptr)
{
    char   *exe_path = NULL;
    size_t  exe_size = 1024;
    ssize_t exe_used;
    char    path_buf[64];
    int     path_len;

    path_len = snprintf(path_buf, sizeof path_buf, "/proc/%ld/exe", (long)pid);
    if (path_len < 1 || path_len >= sizeof path_buf) {
        errno = ENOMEM;
        return NULL;
    }

    while (1) {

        exe_path = malloc(exe_size);
        if (!exe_path) {
            errno = ENOMEM;
            return NULL;
        }

        exe_used = readlink(path_buf, exe_path, exe_size - 1);
        if (exe_used == (ssize_t)-1)
            return NULL;

        if (exe_used < (ssize_t)1) {
            /* Race condition? */
            errno = ENOENT;
            return NULL;
        }

        if (exe_used < (ssize_t)(exe_size - 1))
            break;

        free(exe_path);
        exe_size += 1024;
    }

    /* Try reallocating the exe_path to minimum size.
     * This is optional, and can even fail without
     * any bad effects. */
    {
        char *temp;

        temp = realloc(exe_path, exe_used + 1);
        if (temp) {
            exe_path = temp;
            exe_size = exe_used + 1;
        }
    }

    if (sizeptr)
        *sizeptr = exe_size;

    if (lenptr)
        *lenptr = exe_used;

    exe_path[exe_used] = '\0';
    return exe_path;
}

int main(int argc, char *argv[])
{
    int   arg;
    char *exe;
    long  pid;
    char  dummy;

    if (argc < 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
        printf("\n");
        printf("Usage: %s [ -h | --help ]\n", argv[0]);
        printf("       %s PID [ PID ... ]\n", argv[0]);
        printf("\n");
        return 0;
    }

    for (arg = 1; arg < argc; arg++)
        if (sscanf(argv[arg], " %ld %c", &pid, &dummy) == 1 && pid > 0L) {
            exe = exe_of((pid_t)pid, NULL, NULL);
            if (exe) {
                printf("Process %ld runs '%s'.\n", pid, exe);
                free(exe);
            } else
                printf("Process %ld: %s.\n", pid, strerror(errno));
        } else {
            printf("%s: Invalid PID.\n", argv[arg]);
            return 1;
        }

    return 0;
}

上面,exe_of()函数返回伪符号链接/proc/PID/exe指向的动态分配副本,也可以选择存储分配的大小和/或路径长度。 (上面的示例程序不需要它们,所以它们是NULL。)

这个想法非常简单:为大多数情况分配一个足够大的初始动态指针,但不是非常大。保留字符串结尾NUL字节的最后一个字节。如果readlink()返回的大小与给它的缓冲区长度相同 - 它不会添加一个终止字符串结尾的NUL字节 - 那么缓冲区可能太短;丢弃它,分配更大的缓冲区,然后重试。

同样,如果您希望阅读/proc/下的伪文件的完整内容,则无法首先使用lstat() / stat()来了解您可能需要多大的缓冲区;你需要分配一个缓冲区,尽可能多地读取,并在必要时重新分配一个更大的缓冲区。 (我也可以展示示例代码。)

有问题吗?

答案 1 :(得分:1)

无法对我要回复的帖子发表评论,对不起!

Valgrind将在Nominal Animal的帖子中报告exe_path = malloc(exe_size);,因为'块肯定会丢失'。所以,如果你正在使用那个功能,那么你的记忆力会很快上升。

即使返回NULL也释放内存将解决此问题。当你使用-Wall

时,还应该添加来自malloc调用的显式转换(char *)以使gcc停止抱怨
char* exe_of(const pid_t pid, size_t *const sizeptr, size_t *const lenptr)
{
    char   *exe_path = NULL;
    size_t  exe_size = 1024;
    ssize_t exe_used;
    char    path_buf[64];
    unsigned int path_len;

    path_len = snprintf(path_buf, sizeof path_buf, "/proc/%ld/exe", (long)pid);
    if (path_len < 1 || path_len >= sizeof path_buf) {
        errno = ENOMEM;
        return NULL;
    }

    while (1) {

        exe_path = (char*)malloc(exe_size);
        if (!exe_path) {
            errno = ENOMEM;
            return NULL;
        }

        exe_used = readlink(path_buf, exe_path, exe_size - 1);
        if (exe_used == (ssize_t)-1) {
            free(exe_path);
            return NULL;
        }

        if (exe_used < (ssize_t)1) {
            /* Race condition? */
            errno = ENOENT;
            free(exe_path);
            return NULL;
        }

        if (exe_used < (ssize_t)(exe_size - 1))
            break;

        free(exe_path);
        exe_size += 1024;
    }

    /* Try reallocating the exe_path to minimum size.
     * This is optional, and can even fail without
     * any bad effects. */
    {
        char *temp;

        temp = (char*)realloc(exe_path, exe_used + 1);
        if (temp) {
            exe_path = temp;
            exe_size = exe_used + 1;
        }
    }

    if (sizeptr)
        *sizeptr = exe_size;

    if (lenptr)
        *lenptr = exe_used;

    exe_path[exe_used] = '\0';
    return exe_path;
}

答案 2 :(得分:0)

一般来说,你不必担心这个问题,但在使用Nominal Animal评论后,似乎linux通过PAGE_SIZE来限制来自/ proc / PID / exe的文件路径。因此,即使文件系统支持比这更长的路径,也无法获得readlink()来为您提供该路径,因为这是作为硬限制实现的。我找到了另一种方式,所以IF readlink与ENAMETOOLONG失败,你可以读/ proc / PID / maps,虽然这可能会被击中或错过,我发现它击中了4个不同发行版中的每个进程除了系统进程没有实际文件名(ENOENT)

int getExecutableFromMaps(char *buf, size_t bufsize) {
  FILE *fp;
  char *mylinebuf = NULL;
  size_t mylinebufsize = 0;
  size_t counter = 0;
  size_t start = 0;
  size_t column = 0;
  int result = -1;
  fp = fopen("/proc/self/maps", "r");
  if( fp != NULL ) {
    if( getline(&mylinebuf, &mylinebufsize, fp) >= 0 ) {
      if( mylinebuf != NULL ) {
        while( column < 5 && counter < mylinebufsize ) {
          while( counter < mylinebufsize && mylinebuf[counter] != ' ') {
            counter++;
          }
          while( counter < mylinebufsize && mylinebuf[counter] == ' ') {
            counter++;
          }
          column++;
        }
        int start = counter;
        while( counter < mylinebufsize && (mylinebuf[counter] != '\n' && mylinebuf[counter] != '\r') ) {
          counter++;
        }
        if( counter <= mylinebufsize && start < counter && (counter-start+1)<=bufsize ) {
          memcpy(buf, &mylinebuf[start], counter-start);
          buf[counter-start+1] = 0;
          result = counter-start+1;
        }
      }
    }
    if( mylinebuf != NULL ) {
      free(mylinebuf);
    }
    fclose(fp);
  }
  return result;
}

注意:这是补充代码,只应在readlink失败后调用,因为这很昂贵。