init: service file keyword

am: 62767fe29f

Change-Id: Ib7655bd3a8cbe7e1861e5999c34b02c9efa8b9f7
This commit is contained in:
Mark Salyzyn 2016-11-04 14:43:02 +00:00 committed by android-build-merger
commit cc46af0fc9
9 changed files with 386 additions and 79 deletions

View file

@ -46,6 +46,7 @@ LOCAL_CPPFLAGS := $(init_cflags)
LOCAL_SRC_FILES:= \ LOCAL_SRC_FILES:= \
action.cpp \ action.cpp \
capabilities.cpp \ capabilities.cpp \
descriptors.cpp \
import_parser.cpp \ import_parser.cpp \
init_parser.cpp \ init_parser.cpp \
log.cpp \ log.cpp \
@ -125,8 +126,10 @@ LOCAL_SRC_FILES := \
LOCAL_SHARED_LIBRARIES += \ LOCAL_SHARED_LIBRARIES += \
libcutils \ libcutils \
libbase \ libbase \
libselinux \
LOCAL_STATIC_LIBRARIES := libinit LOCAL_STATIC_LIBRARIES := libinit
LOCAL_SANITIZE := integer LOCAL_SANITIZE := integer
LOCAL_CLANG := true LOCAL_CLANG := true
LOCAL_CPPFLAGS := -Wall -Wextra -Werror
include $(BUILD_NATIVE_TEST) include $(BUILD_NATIVE_TEST)

104
init/descriptors.cpp Normal file
View file

@ -0,0 +1,104 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "descriptors.h"
#include <ctype.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <android-base/stringprintf.h>
#include <cutils/files.h>
#include <cutils/sockets.h>
#include "init.h"
#include "log.h"
#include "util.h"
DescriptorInfo::DescriptorInfo(const std::string& name, const std::string& type, uid_t uid,
gid_t gid, int perm, const std::string& context)
: name_(name), type_(type), uid_(uid), gid_(gid), perm_(perm), context_(context) {
}
DescriptorInfo::~DescriptorInfo() {
}
std::ostream& operator<<(std::ostream& os, const DescriptorInfo& info) {
return os << " descriptors " << info.name_ << " " << info.type_ << " " << std::oct << info.perm_;
}
bool DescriptorInfo::operator==(const DescriptorInfo& other) const {
return name_ == other.name_ && type_ == other.type_ && key() == other.key();
}
void DescriptorInfo::CreateAndPublish(const std::string& globalContext) const {
// Create
const std::string& contextStr = context_.empty() ? globalContext : context_;
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 : '_'; });
std::string val = android::base::StringPrintf("%d", fd);
add_environment(publishedName.c_str(), val.c_str());
// make sure we don't close on exec
fcntl(fd, F_SETFD, 0);
}
void DescriptorInfo::Clean() const {
}
SocketInfo::SocketInfo(const std::string& name, const std::string& type, uid_t uid,
gid_t gid, int perm, const std::string& context)
: DescriptorInfo(name, type, uid, gid, perm, context) {
}
void SocketInfo::Clean() const {
unlink(android::base::StringPrintf(ANDROID_SOCKET_DIR "/%s", name().c_str()).c_str());
}
int SocketInfo::Create(const std::string& context) const {
int flags = ((type() == "stream" ? SOCK_STREAM :
(type() == "dgram" ? SOCK_DGRAM :
SOCK_SEQPACKET)));
return create_socket(name().c_str(), flags, perm(), uid(), gid(), context.c_str());
}
const std::string SocketInfo::key() const {
return ANDROID_SOCKET_ENV_PREFIX;
}
FileInfo::FileInfo(const std::string& name, const std::string& type, uid_t uid,
gid_t gid, int perm, const std::string& context)
: DescriptorInfo(name, type, uid, gid, perm, context) {
}
int FileInfo::Create(const std::string& context) const {
int flags = ((type() == "r" ? O_RDONLY :
(type() == "w" ? (O_WRONLY | O_CREAT) :
(O_RDWR | O_CREAT))));
return create_file(name().c_str(), flags, perm(), uid(), gid(), context.c_str());
}
const std::string FileInfo::key() const {
return ANDROID_FILE_ENV_PREFIX;
}

78
init/descriptors.h Normal file
View file

@ -0,0 +1,78 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _INIT_DESCRIPTORS_H
#define _INIT_DESCRIPTORS_H
#include <sys/types.h>
#include <string>
class DescriptorInfo {
public:
DescriptorInfo(const std::string& name, const std::string& type, uid_t uid,
gid_t gid, int perm, const std::string& context);
virtual ~DescriptorInfo();
friend std::ostream& operator<<(std::ostream& os, const class DescriptorInfo& info);
bool operator==(const DescriptorInfo& other) const;
void CreateAndPublish(const std::string& globalContext) const;
virtual void Clean() const;
protected:
const std::string& name() const { return name_; }
const std::string& type() const { return type_; }
uid_t uid() const { return uid_; }
gid_t gid() const { return gid_; }
int perm() const { return perm_; }
const std::string& context() const { return context_; }
private:
std::string name_;
std::string type_;
uid_t uid_;
gid_t gid_;
int perm_;
std::string context_;
virtual int Create(const std::string& globalContext) const = 0;
virtual const std::string key() const = 0;
};
std::ostream& operator<<(std::ostream& os, const DescriptorInfo& info);
class SocketInfo : public DescriptorInfo {
public:
SocketInfo(const std::string& name, const std::string& type, uid_t uid,
gid_t gid, int perm, const std::string& context);
void Clean() const override;
private:
virtual int Create(const std::string& context) const override;
virtual const std::string key() const override;
};
class FileInfo : public DescriptorInfo {
public:
FileInfo(const std::string& name, const std::string& type, uid_t uid,
gid_t gid, int perm, const std::string& context);
private:
virtual int Create(const std::string& context) const override;
virtual const std::string key() const override;
};
#endif

View file

@ -141,12 +141,20 @@ setenv <name> <value>
Set the environment variable <name> to <value> in the launched process. Set the environment variable <name> to <value> in the launched process.
socket <name> <type> <perm> [ <user> [ <group> [ <seclabel> ] ] ] socket <name> <type> <perm> [ <user> [ <group> [ <seclabel> ] ] ]
Create a unix domain socket named /dev/socket/<name> and pass Create a unix domain socket named /dev/socket/<name> and pass its fd to the
its fd to the launched process. <type> must be "dgram", "stream" or "seqpacket". launched process. <type> must be "dgram", "stream" or "seqpacket". User and
User and group default to 0. group default to 0. 'seclabel' is the SELinux security context for the
'seclabel' is the SELinux security context for the socket. socket. It defaults to the service security context, as specified by
It defaults to the service security context, as specified by seclabel or seclabel or computed based on the service executable file security context.
computed based on the service executable file security context. For native executables see libcutils android_get_control_socket().
file <path> <type> <perm> [ <user> [ <group> [ <seclabel> ] ] ]
Open/Create a file path and pass its fd to the launched process. <type> must
be "r", "w" or "rw". User and group default to 0. 'seclabel' is the SELinux
security context for the file if it must be created. It defaults to the
service security context, as specified by seclabel or computed based on the
service executable file security context. For native executables see
libcutils android_get_control_file().
user <username> user <username>
Change to 'username' before exec'ing this service. Change to 'username' before exec'ing this service.

View file

@ -36,7 +36,6 @@
#include <android-base/stringprintf.h> #include <android-base/stringprintf.h>
#include <android-base/strings.h> #include <android-base/strings.h>
#include <cutils/android_reboot.h> #include <cutils/android_reboot.h>
#include <cutils/sockets.h>
#include <system/thread_defs.h> #include <system/thread_defs.h>
#include <processgroup/processgroup.h> #include <processgroup/processgroup.h>
@ -145,14 +144,6 @@ static void ExpandArgs(const std::vector<std::string>& args, std::vector<char*>*
strs->push_back(nullptr); strs->push_back(nullptr);
} }
SocketInfo::SocketInfo() : uid(0), gid(0), perm(0) {
}
SocketInfo::SocketInfo(const std::string& name, const std::string& type, uid_t uid,
gid_t gid, int perm, const std::string& socketcon)
: name(name), type(type), uid(uid), gid(gid), perm(perm), socketcon(socketcon) {
}
ServiceEnvironmentInfo::ServiceEnvironmentInfo() { ServiceEnvironmentInfo::ServiceEnvironmentInfo() {
} }
@ -213,20 +204,6 @@ void Service::KillProcessGroup(int signal) {
} }
} }
void Service::CreateSockets(const std::string& context) {
for (const auto& si : sockets_) {
int socket_type = ((si.type == "stream" ? SOCK_STREAM :
(si.type == "dgram" ? SOCK_DGRAM :
SOCK_SEQPACKET)));
const char* socketcon = !si.socketcon.empty() ? si.socketcon.c_str() : context.c_str();
int s = create_socket(si.name.c_str(), socket_type, si.perm, si.uid, si.gid, socketcon);
if (s >= 0) {
PublishSocket(si.name, s);
}
}
}
void Service::SetProcessAttributes() { void Service::SetProcessAttributes() {
// Keep capabilites on uid change. // Keep capabilites on uid change.
if (capabilities_.any() && uid_) { if (capabilities_.any() && uid_) {
@ -273,11 +250,9 @@ bool Service::Reap() {
KillProcessGroup(SIGKILL); KillProcessGroup(SIGKILL);
} }
// Remove any sockets we may have created. // Remove any descriptor resources we may have created.
for (const auto& si : sockets_) { std::for_each(descriptors_.begin(), descriptors_.end(),
std::string tmp = StringPrintf(ANDROID_SOCKET_DIR "/%s", si.name.c_str()); std::bind(&DescriptorInfo::Clean, std::placeholders::_1));
unlink(tmp.c_str());
}
if (flags_ & SVC_EXEC) { if (flags_ & SVC_EXEC) {
LOG(INFO) << "SVC_EXEC pid " << pid_ << " finished..."; LOG(INFO) << "SVC_EXEC pid " << pid_ << " finished...";
@ -330,9 +305,8 @@ void Service::DumpState() const {
LOG(INFO) << "service " << name_; LOG(INFO) << "service " << name_;
LOG(INFO) << " class '" << classname_ << "'"; LOG(INFO) << " class '" << classname_ << "'";
LOG(INFO) << " exec "<< android::base::Join(args_, " "); LOG(INFO) << " exec "<< android::base::Join(args_, " ");
for (const auto& si : sockets_) { std::for_each(descriptors_.begin(), descriptors_.end(),
LOG(INFO) << " socket " << si.name << " " << si.type << " " << std::oct << si.perm; [] (const auto& info) { LOG(INFO) << *info; });
}
} }
bool Service::ParseCapabilities(const std::vector<std::string>& args, std::string* err) { bool Service::ParseCapabilities(const std::vector<std::string>& args, std::string* err) {
@ -469,20 +443,48 @@ bool Service::ParseSetenv(const std::vector<std::string>& args, std::string* err
return true; return true;
} }
/* name type perm [ uid gid context ] */ template <typename T>
bool Service::AddDescriptor(const std::vector<std::string>& args, std::string* err) {
int perm = args.size() > 3 ? std::strtoul(args[3].c_str(), 0, 8) : -1;
uid_t uid = args.size() > 4 ? decode_uid(args[4].c_str()) : 0;
gid_t gid = args.size() > 5 ? decode_uid(args[5].c_str()) : 0;
std::string context = args.size() > 6 ? args[6] : "";
auto descriptor = std::make_unique<T>(args[1], args[2], uid, gid, perm, context);
auto old =
std::find_if(descriptors_.begin(), descriptors_.end(),
[&descriptor] (const auto& other) { return descriptor.get() == other.get(); });
if (old != descriptors_.end()) {
*err = "duplicate descriptor " + args[1] + " " + args[2];
return false;
}
descriptors_.emplace_back(std::move(descriptor));
return true;
}
// name type perm [ uid gid context ]
bool Service::ParseSocket(const std::vector<std::string>& args, std::string* err) { bool Service::ParseSocket(const std::vector<std::string>& args, std::string* err) {
if (args[2] != "dgram" && args[2] != "stream" && args[2] != "seqpacket") { if (args[2] != "dgram" && args[2] != "stream" && args[2] != "seqpacket") {
*err = "socket type must be 'dgram', 'stream' or 'seqpacket'"; *err = "socket type must be 'dgram', 'stream' or 'seqpacket'";
return false; return false;
} }
return AddDescriptor<SocketInfo>(args, err);
}
int perm = std::strtoul(args[3].c_str(), 0, 8); // name type perm [ uid gid context ]
uid_t uid = args.size() > 4 ? decode_uid(args[4].c_str()) : 0; bool Service::ParseFile(const std::vector<std::string>& args, std::string* err) {
gid_t gid = args.size() > 5 ? decode_uid(args[5].c_str()) : 0; if (args[2] != "r" && args[2] != "w" && args[2] != "rw") {
std::string socketcon = args.size() > 6 ? args[6] : ""; *err = "file type must be 'r', 'w' or 'rw'";
return false;
sockets_.emplace_back(args[1], args[2], uid, gid, perm, socketcon); }
return true; if ((args[1][0] != '/') || (args[1].find("../") != std::string::npos)) {
*err = "file name must not be relative";
return false;
}
return AddDescriptor<FileInfo>(args, err);
} }
bool Service::ParseUser(const std::vector<std::string>& args, std::string* err) { bool Service::ParseUser(const std::vector<std::string>& args, std::string* err) {
@ -524,6 +526,7 @@ Service::OptionParserMap::Map& Service::OptionParserMap::map() const {
{"seclabel", {1, 1, &Service::ParseSeclabel}}, {"seclabel", {1, 1, &Service::ParseSeclabel}},
{"setenv", {2, 2, &Service::ParseSetenv}}, {"setenv", {2, 2, &Service::ParseSetenv}},
{"socket", {3, 6, &Service::ParseSocket}}, {"socket", {3, 6, &Service::ParseSocket}},
{"file", {2, 6, &Service::ParseFile}},
{"user", {1, 1, &Service::ParseUser}}, {"user", {1, 1, &Service::ParseUser}},
{"writepid", {1, kMax, &Service::ParseWritepid}}, {"writepid", {1, kMax, &Service::ParseWritepid}},
}; };
@ -613,7 +616,8 @@ bool Service::Start() {
add_environment(ei.name.c_str(), ei.value.c_str()); add_environment(ei.name.c_str(), ei.value.c_str());
} }
CreateSockets(scon); std::for_each(descriptors_.begin(), descriptors_.end(),
std::bind(&DescriptorInfo::CreateAndPublish, std::placeholders::_1, scon));
std::string pid_str = StringPrintf("%d", getpid()); std::string pid_str = StringPrintf("%d", getpid());
for (const auto& file : writepid_files_) { for (const auto& file : writepid_files_) {
@ -787,15 +791,6 @@ void Service::OpenConsole() const {
close(fd); close(fd);
} }
void Service::PublishSocket(const std::string& name, int fd) const {
std::string key = StringPrintf(ANDROID_SOCKET_ENV_PREFIX "%s", name.c_str());
std::string val = StringPrintf("%d", fd);
add_environment(key.c_str(), val.c_str());
/* make sure we don't close-on-exec */
fcntl(fd, F_SETFD, 0);
}
int ServiceManager::exec_count_ = 0; int ServiceManager::exec_count_ = 0;
ServiceManager::ServiceManager() { ServiceManager::ServiceManager() {

View file

@ -27,6 +27,7 @@
#include "action.h" #include "action.h"
#include "capabilities.h" #include "capabilities.h"
#include "descriptors.h"
#include "init_parser.h" #include "init_parser.h"
#include "keyword_map.h" #include "keyword_map.h"
@ -48,18 +49,6 @@
class Action; class Action;
class ServiceManager; class ServiceManager;
struct SocketInfo {
SocketInfo();
SocketInfo(const std::string& name, const std::string& type, uid_t uid,
gid_t gid, int perm, const std::string& socketcon);
std::string name;
std::string type;
uid_t uid;
gid_t gid;
int perm;
std::string socketcon;
};
struct ServiceEnvironmentInfo { struct ServiceEnvironmentInfo {
ServiceEnvironmentInfo(); ServiceEnvironmentInfo();
ServiceEnvironmentInfo(const std::string& name, const std::string& value); ServiceEnvironmentInfo(const std::string& name, const std::string& value);
@ -113,9 +102,7 @@ private:
void StopOrReset(int how); void StopOrReset(int how);
void ZapStdio() const; void ZapStdio() const;
void OpenConsole() const; void OpenConsole() const;
void PublishSocket(const std::string& name, int fd) const;
void KillProcessGroup(int signal); void KillProcessGroup(int signal);
void CreateSockets(const std::string& scon);
void SetProcessAttributes(); void SetProcessAttributes();
bool ParseCapabilities(const std::vector<std::string>& args, std::string *err); bool ParseCapabilities(const std::vector<std::string>& args, std::string *err);
@ -134,9 +121,13 @@ private:
bool ParseSeclabel(const std::vector<std::string>& args, std::string* err); bool ParseSeclabel(const std::vector<std::string>& args, std::string* err);
bool ParseSetenv(const std::vector<std::string>& args, std::string* err); bool ParseSetenv(const std::vector<std::string>& args, std::string* err);
bool ParseSocket(const std::vector<std::string>& args, std::string* err); bool ParseSocket(const std::vector<std::string>& args, std::string* err);
bool ParseFile(const std::vector<std::string>& args, std::string* err);
bool ParseUser(const std::vector<std::string>& args, std::string* err); bool ParseUser(const std::vector<std::string>& args, std::string* err);
bool ParseWritepid(const std::vector<std::string>& args, std::string* err); bool ParseWritepid(const std::vector<std::string>& args, std::string* err);
template <typename T>
bool AddDescriptor(const std::vector<std::string>& args, std::string* err);
std::string name_; std::string name_;
std::string classname_; std::string classname_;
std::string console_; std::string console_;
@ -155,7 +146,7 @@ private:
std::string seclabel_; std::string seclabel_;
std::vector<SocketInfo> sockets_; std::vector<std::unique_ptr<DescriptorInfo>> descriptors_;
std::vector<ServiceEnvironmentInfo> envvars_; std::vector<ServiceEnvironmentInfo> envvars_;
Action onrestart_; // Commands to execute on restart. Action onrestart_; // Commands to execute on restart.

View file

@ -14,32 +14,32 @@
* limitations under the License. * limitations under the License.
*/ */
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <ftw.h>
#include <pwd.h>
#include <stdarg.h> #include <stdarg.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <fcntl.h>
#include <ctype.h>
#include <errno.h>
#include <time.h> #include <time.h>
#include <ftw.h> #include <unistd.h>
#include <pwd.h>
#include <selinux/label.h>
#include <selinux/android.h> #include <selinux/android.h>
#include <selinux/label.h>
#include <sys/socket.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h> #include <sys/un.h>
#include <android-base/file.h> #include <android-base/file.h>
#include <android-base/logging.h> #include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h> #include <android-base/strings.h>
/* for ANDROID_SOCKET_* */ /* for ANDROID_SOCKET_* */
#include <cutils/sockets.h> #include <cutils/sockets.h>
#include <android-base/stringprintf.h>
#include "init.h" #include "init.h"
#include "log.h" #include "log.h"
@ -164,6 +164,76 @@ out_close:
return -1; return -1;
} }
/*
* create_file - opens and creates a file as dictated in init.rc.
* This file is inherited by the daemon. We communicate the file
* descriptor's value via the environment variable ANDROID_FILE_<basename>
*/
int create_file(const char *path, int flags, mode_t perm, uid_t uid,
gid_t gid, const char *filecon)
{
char *secontext = NULL;
int ret;
if (filecon) {
if (setsockcreatecon(filecon) == -1) {
PLOG(ERROR) << "setsockcreatecon(\"" << filecon << "\") failed";
return -1;
}
} else if (sehandle) {
ret = selabel_lookup(sehandle, &secontext, path, perm);
if (ret != -1) {
ret = setfscreatecon(secontext);
if (ret == -1) {
freecon(secontext);
PLOG(ERROR) << "setfscreatecon(\"" << secontext << "\") failed";
return -1;
}
}
}
int fd = TEMP_FAILURE_RETRY(open(path, flags | O_NDELAY, perm));
if (filecon) {
setsockcreatecon(NULL);
lsetfilecon(path, filecon);
} else {
setfscreatecon(NULL);
freecon(secontext);
}
if (fd == -1) {
PLOG(ERROR) << "Failed to open/create file '" << path << "'";
goto out_close;
}
if (!(flags & O_NDELAY)) fcntl(fd, F_SETFD, flags);
ret = lchown(path, uid, gid);
if (ret) {
PLOG(ERROR) << "Failed to lchown file '" << path << "'";
goto out_close;
}
if (perm != static_cast<mode_t>(-1)) {
ret = fchmodat(AT_FDCWD, path, perm, AT_SYMLINK_NOFOLLOW);
if (ret) {
PLOG(ERROR) << "Failed to fchmodat file '" << path << "'";
goto out_close;
}
}
LOG(INFO) << "Created file '" << path << "'"
<< ", mode " << std::oct << perm << std::dec
<< ", user " << uid
<< ", group " << gid;
return fd;
out_close:
if (fd >= 0) close(fd);
return -1;
}
bool read_file(const char* path, std::string* content) { bool read_file(const char* path, std::string* content) {
content->clear(); content->clear();

View file

@ -27,6 +27,8 @@
int create_socket(const char *name, int type, mode_t perm, int create_socket(const char *name, int type, mode_t perm,
uid_t uid, gid_t gid, const char *socketcon); uid_t uid, gid_t gid, const char *socketcon);
int create_file(const char *path, int mode, mode_t perm,
uid_t uid, gid_t gid, const char *filecon);
bool read_file(const char* path, std::string* content); bool read_file(const char* path, std::string* content);
int write_file(const char* path, const char* content); int write_file(const char* path, const char* content);

View file

@ -17,7 +17,15 @@
#include "util.h" #include "util.h"
#include <errno.h> #include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <cutils/files.h>
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <selinux/android.h>
TEST(util, read_file_ENOENT) { TEST(util, read_file_ENOENT) {
std::string s("hello"); std::string s("hello");
@ -41,3 +49,51 @@ TEST(util, decode_uid) {
EXPECT_EQ(UINT_MAX, decode_uid("toot")); EXPECT_EQ(UINT_MAX, decode_uid("toot"));
EXPECT_EQ(123U, decode_uid("123")); EXPECT_EQ(123U, decode_uid("123"));
} }
struct selabel_handle *sehandle;
TEST(util, create_file) {
if (!sehandle) sehandle = selinux_android_file_context_handle();
static const char path[] = "/data/local/tmp/util.create_file.test";
static const char key[] = ANDROID_FILE_ENV_PREFIX "_data_local_tmp_util_create_file_test";
EXPECT_EQ(unsetenv(key), 0);
unlink(path);
int fd;
uid_t uid = decode_uid("logd");
gid_t gid = decode_uid("system");
mode_t perms = S_IRWXU | S_IWGRP | S_IRGRP | S_IROTH;
static const char context[] = "u:object_r:misc_logd_file:s0";
EXPECT_GE(fd = create_file(path, O_RDWR | O_CREAT, perms, uid, gid, context), 0);
if (fd < 0) return;
static const char hello[] = "hello world\n";
static const ssize_t len = strlen(hello);
EXPECT_EQ(write(fd, hello, len), len);
char buffer[sizeof(hello)];
memset(buffer, 0, sizeof(buffer));
EXPECT_GE(lseek(fd, 0, SEEK_SET), 0);
EXPECT_EQ(read(fd, buffer, sizeof(buffer)), len);
EXPECT_EQ(strcmp(hello, buffer), 0);
char val[32];
snprintf(val, sizeof(val), "%d", fd);
EXPECT_EQ(android_get_control_file(path), -1);
setenv(key, val, true);
EXPECT_EQ(android_get_control_file(path), fd);
close(fd);
EXPECT_EQ(android_get_control_file(path), -1);
EXPECT_EQ(unsetenv(key), 0);
struct stat st;
EXPECT_EQ(stat(path, &st), 0);
EXPECT_EQ(st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO), perms);
EXPECT_EQ(st.st_uid, uid);
EXPECT_EQ(st.st_gid, gid);
security_context_t con;
EXPECT_GE(getfilecon(path, &con), 0);
EXPECT_NE(con, static_cast<security_context_t>(NULL));
if (con) {
EXPECT_EQ(context, std::string(con));
}
freecon(con);
EXPECT_EQ(unlink(path), 0);
}