我正在尝试在 Docker 容器内运行 FlexLM 许可证服务器。我以前这样做过,没有任何问题。但现在我有一个供应商(Synopsys)守护进程(snpslmd),它似乎与 Docker 配合得不好。守护进程抱怨无法打开其锁定文件:/var/tmp/locksnpslmd
,但/var/tmp
存在且可写。
以下是 Docker 容器中供应商守护程序的 strace:
munmap(0x7fa322830000, 167936) = 0
setsid() = 64
openat(AT_FDCWD, "/", O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC) = 3
getdents(3, /* 22 entries */, 32768) = 568
getdents(3, /* 0 entries */, 32768) = 0
close(3) = 0
write(1, "17:35:24 (snpslmd) Cannot open d"..., 4817:35:24 (snpslmd) Cannot open daemon lock file
) = 48
write(1, "17:35:24 (snpslmd) EXITING DUE T"..., 5817:35:24 (snpslmd) EXITING DUE TO SIGNAL 41 Exit reason 9
) = 58
exit_group(41) = ?
+++ exited with 41 +++**
下面是在容器外正常运行的供应商守护程序的相同 strace 段:
munmap(0x7f25da4db000, 4096) = 0
setsid() = 15808
openat(AT_FDCWD, "/", O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC) = 3
getdents(3, /* 23 entries */, 32768) = 584
getdents(3, /* 0 entries */, 32768) = 0
close(3) = 0
open("/var/tmp/locksnpslmd", O_RDWR|O_CREAT, 0666) = 3
fcntl(3, F_SETLK, {l_type=F_WRLCK, l_whence=SEEK_CUR, l_start=0, l_len=0}) = 0
stat("/var/tmp/locksnpslmd", {st_mode=S_IFREG|0644, st_size=0, ...}) = 0
有任何想法吗?
答案1
发生这种情况的原因是 getdents() 返回“/”子目录“.”和“..”的不同 inode 编号。可以使用 snpslmd 的 LD_PRELOAD hack 来修复此问题
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <dlfcn.h>
#include <string.h>
static int is_root = 0;
static int d_ino = -1;
static DIR *(*orig_opendir)(const char *name);
static int (*orig_closedir)(DIR *dirp);
static struct dirent *(*orig_readdir)(DIR *dirp);
DIR *opendir(const char *name) {
if (strcmp(name, "/") == 0) {
is_root = 1;
}
return orig_opendir(name);
}
int closedir(DIR *dirp) {
is_root = 0;
return orig_closedir(dirp);
}
struct dirent *readdir(DIR *dirp) {
struct dirent *r = orig_readdir(dirp);
if (is_root && r) {
if (strcmp(r->d_name, ".") == 0) {
r->d_ino = d_ino;
} else if (strcmp(r->d_name, "..") == 0) {
r->d_ino = d_ino;
}
}
return r;
}
static __attribute__((constructor)) void init_methods() {
orig_opendir = dlsym(RTLD_NEXT, "opendir");
orig_closedir = dlsym(RTLD_NEXT, "closedir");
orig_readdir = dlsym(RTLD_NEXT, "readdir");
DIR *d = orig_opendir("/");
struct dirent *e = orig_readdir(d);
while (e) {
if (strcmp(e->d_name, ".") == 0) {
d_ino = e->d_ino;
break;
}
e = orig_readdir(d);
}
orig_closedir(d);
if (d_ino == -1) {
puts("Failed to determine root directory inode number");
exit(EXIT_FAILURE);
}
}
使用以下方式编译
gcc -ldl -shared -fPIC snpslmd-hack.c -o snpslmd-hack.so
使用包装脚本代替原始 snpslmd:
#!/bin/sh
export LD_PRELOAD=snpslmd-hack.so
exec /usr/bin/snpslmd_bin "$@"