源码中的 rc 文件里面定义了很多 socket:
bash
# ./system/core/logd/logd.rc
service logd /system/bin/logd
socket logd stream 0666 logd logd
socket logdr seqpacket 0666 logd logd
socket logdw dgram+passcred 0222 logd logd
file /proc/kmsg r
file /dev/kmsg w
user logd
group logd system package_info readproc
capabilities SYSLOG AUDIT_CONTROL SETGID
writepid /dev/cpuset/system-background/tasks
socket 命令的格式如下:
bash
socket <name> <type> <perm> [ <user> [ <group> [ <seclabel> ] ] ]
name: 名称
type: 类型,可以取值为 stream、dgram 或 seqpacket
perm: 权限
user: 用户
group: 组
-
socket 命令会创建一个 unix domain socket, 路径为
/dev/socket/name
, 并将 fd 保存在环境变量中。 -
type 只能是 "dgram", "stream" or "seqpacket"。
-
user 和 group 默认值是 0。
-
seclabel 是这个 socket 的 SELinux security context, 它的默认值是 service 的 security context 或者基于其可执行文件的 security context。
-
在代码中,可以通过 libcutils 库提供的 android_get_control_socket 函数来获取这个 socket 的 fd。
/dev/socket/
目录下有很多文件:
bash
ls /dev/socket/
adbd logd property_service traced_consumer zygote
audioserver logdr statsdw traced_producer zygote_secondary
dnsproxyd logdw tombstoned_crash usap_pool_primary
fwmarkd mdns tombstoned_intercept usap_pool_secondary
lmkd mdnsd tombstoned_java_trace wpa_wlan0
这些文件都是由 init 程序解析 rc 文件生产的。
我们来看一下生成过程:
解析 rc 文件的过程中会调用 CreateAndPublish 函数创建 socket:
cpp
// system/core/init/descriptors.cpp
void DescriptorInfo::CreateAndPublish(const std::string& globalContext) const {
// Create
const std::string& contextStr = context_.empty() ? globalContext : context_;
// 调用 Create 初始化 socket
int fd = Create(contextStr);
if (fd < 0) return;
// Publish
std::string publishedName = key() + name_;
std::for_each(publishedName.begin(), publishedName.end(),
[] (char& c) { c = isalnum(c) ? c : '_'; });
// 将 socket 的名字与 fd 保存到环境变量中
std::string val = std::to_string(fd);
setenv(publishedName.c_str(), val.c_str(), 1);
// make sure we don't close on exec
fcntl(fd, F_SETFD, 0);
}
// 进一步调用 CreateSocket
int SocketInfo::Create(const std::string& context) const {
auto types = android::base::Split(type(), "+");
int flags =
((types[0] == "stream" ? SOCK_STREAM : (types[0] == "dgram" ? SOCK_DGRAM : SOCK_SEQPACKET)));
bool passcred = types.size() > 1 && types[1] == "passcred";
// 进一步调用 CreateSocket
return CreateSocket(name().c_str(), flags, passcred, perm(), uid(), gid(), context.c_str());
}
// 初始化 socket
int CreateSocket(const char* name, int type, bool passcred, mode_t perm, uid_t uid, gid_t gid,
const char* socketcon) {
if (socketcon) {
if (setsockcreatecon(socketcon) == -1) {
PLOG(ERROR) << "setsockcreatecon(\"" << socketcon << "\") failed";
return -1;
}
}
// 初始化 Unix domain socket
android::base::unique_fd fd(socket(PF_UNIX, type, 0));
if (fd < 0) {
PLOG(ERROR) << "Failed to open socket '" << name << "'";
return -1;
}
if (socketcon) setsockcreatecon(NULL);
// 地址家族为 AF_UNIX,ANDROID_SOCKET_DIR 为 /dev/socket
struct sockaddr_un addr;
memset(&addr, 0 , sizeof(addr));
addr.sun_family = AF_UNIX;
snprintf(addr.sun_path, sizeof(addr.sun_path), "/%s",
name);
if ((unlink(addr.sun_path) != 0) && (errno != ENOENT)) {
PLOG(ERROR) << "Failed to unlink old socket '" << name << "'";
return -1;
}
std::string secontext;
if (SelabelLookupFileContext(addr.sun_path, S_IFSOCK, &secontext) && !secontext.empty()) {
setfscreatecon(secontext.c_str());
}
if (passcred) {
int on = 1;
if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on))) {
PLOG(ERROR) << "Failed to set SO_PASSCRED '" << name << "'";
return -1;
}
}
// 绑定本地地址
int ret = bind(fd, (struct sockaddr *) &addr, sizeof (addr));
int savederrno = errno;
if (!secontext.empty()) {
setfscreatecon(nullptr);
}
if (ret) {
errno = savederrno;
PLOG(ERROR) << "Failed to bind socket '" << name << "'";
goto out_unlink;
}
// 根据声明修改 owner 和 group
if (lchown(addr.sun_path, uid, gid)) {
PLOG(ERROR) << "Failed to lchown socket '" << addr.sun_path << "'";
goto out_unlink;
}
// 根据声明修改权限
if (fchmodat(AT_FDCWD, addr.sun_path, perm, AT_SYMLINK_NOFOLLOW)) {
PLOG(ERROR) << "Failed to fchmodat socket '" << addr.sun_path << "'";
goto out_unlink;
}
LOG(INFO) << "Created socket '" << addr.sun_path << "'"
<< ", mode " << std::oct << perm << std::dec
<< ", user " << uid
<< ", group " << gid;
return fd.release();
out_unlink:
unlink(addr.sun_path);
return -1;
}
对于在 rc 文件中配置好的 socket,在系统启动时就初始化好了,当我们使用 socket 的时候,可以直接通过 android_get_control_socket
函数从环境变量中获取到 socket fd:
cpp
int android_get_control_socket(const char* name) {
// 从环境变量中获取 socket 对应的 fd
int fd = __android_get_control_from_env(ANDROID_SOCKET_ENV_PREFIX, name);
if (fd < 0) return fd;
// Compare to UNIX domain socket name, must match!
struct sockaddr_un addr;
socklen_t addrlen = sizeof(addr);
int ret = getsockname(fd, (struct sockaddr*)&addr, &addrlen);
if (ret < 0) return -1;
constexpr char prefix[] = ANDROID_SOCKET_DIR "/";
constexpr size_t prefix_size = sizeof(prefix) - sizeof('\0');
if ((strncmp(addr.sun_path, prefix, prefix_size) == 0) &&
(strcmp(addr.sun_path + prefix_size, name) == 0)) {
// It is what we think it is
return fd;
}
return -1;
}
参考资料
关于
我叫阿豪,2015 年本科毕业于国防科学技术大学指挥信息系统专业,毕业后从事信息化装备的研发工作,工作内容主要涉及 Android Framework 与 Linux Kernel。
如果你对 Android Framework 感兴趣或者正在学习 Android Framework,可以关注我的微信公众号和抖音,我会持续分享我的学习经验,帮助正在学习的你少走一些弯路。学习过程中如果你有疑问或者你的经验想要分享给大家可以添加我的微信,我拉你进技术交流群。