Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2e4c85f1 authored by Tom Cherry's avatar Tom Cherry
Browse files

init: clean up file / socket descriptor creation

clang-tidy hinted that some of this code wasn't right.  Looking
deeper, there is really not much related to file and socket
descriptors, except that they're published in similar ways to the
environment.  All of the abstraction into a 'Descriptor' class takes
us further away from specifying what we really mean.

This removes that abstraction, adds stricter checks and better errors
for parsing init scripts, reports sockets and files that are unable to
be acquired before exec, and updates the README.md for the passcred
option.

Test: build, logd (uses files and sockets) works
Change-Id: I59e611e95c85bdbefa779ef69b32b9dd4ee203e2
parent 8a779ee9
Loading
Loading
Loading
Loading
+0 −2
Original line number Diff line number Diff line
@@ -107,7 +107,6 @@ cc_library_static {
        "bootchart.cpp",
        "builtins.cpp",
        "capabilities.cpp",
        "descriptors.cpp",
        "devices.cpp",
        "epoll.cpp",
        "firmware_handler.cpp",
@@ -254,7 +253,6 @@ cc_binary {
        "action_manager.cpp",
        "action_parser.cpp",
        "capabilities.cpp",
        "descriptors.cpp",
        "epoll.cpp",
        "keychords.cpp",
        "import_parser.cpp",
+2 −1
Original line number Diff line number Diff line
@@ -300,7 +300,8 @@ runs the service.

`socket <name> <type> <perm> [ <user> [ <group> [ <seclabel> ] ] ]`
> Create a UNIX domain socket named /dev/socket/_name_ and pass its fd to the
  launched process.  _type_ must be "dgram", "stream" or "seqpacket".  User and
  launched process.  _type_ must be "dgram", "stream" or "seqpacket".  _type_
  may end with "+passcred" to enable SO_PASSCRED on the socket. User and
  group default to 0.  'seclabel' is the SELinux security context for the
  socket.  It defaults to the service security context, as specified by
  seclabel or computed based on the service executable file security context.

init/descriptors.cpp

deleted100644 → 0
+0 −132
Original line number Diff line number Diff line
/*
 * 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 <unistd.h>

#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <cutils/android_get_control_file.h>
#include <cutils/sockets.h>

#include "util.h"

namespace android {
namespace init {

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 = 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);
}

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 {
    std::string path = android::base::StringPrintf("%s/%s", ANDROID_SOCKET_DIR, name().c_str());
    unlink(path.c_str());
}

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";
    return CreateSocket(name().c_str(), flags, passcred, 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)
        // defaults OK for uid,..., they are ignored for this class.
        : DescriptorInfo(name, type, uid, gid, perm, context) {
}

int FileInfo::Create(const std::string&) const {
  int flags = (type() == "r") ? O_RDONLY :
              (type() == "w") ? O_WRONLY :
                                O_RDWR;

  // Make sure we do not block on open (eg: devices can chose to block on
  // carrier detect).  Our intention is never to delay launch of a service
  // for such a condition.  The service can perform its own blocking on
  // carrier detect.
  android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(name().c_str(),
                                                      flags | O_NONBLOCK)));

  if (fd < 0) {
    PLOG(ERROR) << "Failed to open file '" << name().c_str() << "'";
    return -1;
  }

  // Fixup as we set O_NONBLOCK for open, the intent for fd is to block reads.
  fcntl(fd, F_SETFL, flags);

  LOG(INFO) << "Opened file '" << name().c_str() << "'"
            << ", flags " << std::oct << flags << std::dec;

  return fd.release();
}

const std::string FileInfo::key() const {
  return ANDROID_FILE_ENV_PREFIX;
}

}  // namespace init
}  // namespace android

init/descriptors.h

deleted100644 → 0
+0 −84
Original line number Diff line number Diff line
/*
 * 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>

namespace android {
namespace init {

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;
};

}  // namespace init
}  // namespace android

#endif
+5 −4
Original line number Diff line number Diff line
@@ -994,10 +994,11 @@ void CreateSerializedPropertyInfo() {
void StartPropertyService(Epoll* epoll) {
    property_set("ro.property_service.version", "2");

    property_set_fd = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
                                   false, 0666, 0, 0, nullptr);
    if (property_set_fd == -1) {
        PLOG(FATAL) << "start_property_service socket creation failed";
    if (auto result = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
                                   false, 0666, 0, 0, {})) {
        property_set_fd = *result;
    } else {
        PLOG(FATAL) << "start_property_service socket creation failed: " << result.error();
    }

    listen(property_set_fd, 8);
Loading