#include "control.h"
#include "dinit-log.h"
#include "dinit-socket.h"
+#include "static-string.h"
+
+#include "mconfig.h"
/*
* When running as the system init process, Dinit processes the following signals:
* services even if the halt/reboot commands are unavailable for some reason.
*/
+using namespace cts;
+
using eventloop_t = dasynq::event_loop<dasynq::null_mutex>;
eventloop_t event_loop;
// Control socket path. We maintain a string (control_socket_str) in case we need
// to allocate storage, but control_socket_path is the authoritative value.
-static const char *control_socket_path = "/dev/dinitctl";
+static const char *control_socket_path = SYSCONTROLSOCKET;
static std::string control_socket_str;
static const char *env_file_path = "/etc/dinit/environment";
-static const char *log_socket_path = "/dev/log";
+static const char *log_path = "/dev/log";
+static bool log_is_syslog = true; // if false, log is a file
static const char *user_home_path = nullptr;
am_system_init = (getpid() == 1);
const char * service_dir = nullptr;
+ bool service_dir_dynamic = false; // service_dir dynamically allocated?
const char * env_file = nullptr;
string service_dir_str; // to hold storage for above if necessary
bool control_socket_path_set = false;
return 1;
}
}
+ else if (strcmp(argv[i], "--log-file") == 0 || strcmp(argv[i], "-l") == 0) {
+ if (++i < argc) {
+ log_path = argv[i];
+ log_is_syslog = false;
+ }
+ else {
+ cerr << "dinit: '--log-file' (-l) requires an argument" << endl;
+ return 1;
+ }
+ }
else if (strcmp(argv[i], "--help") == 0) {
cout << "dinit, an init with dependency management" << endl;
cout << " --help display help" << endl;
if (service_dir == nullptr && ! am_system_init) {
const char * userhome = get_user_home();
if (userhome != nullptr) {
- service_dir_str = get_user_home();
- service_dir_str += "/dinit.d";
- service_dir = service_dir_str.c_str();
+ const char * user_home = get_user_home();
+ size_t user_home_len = strlen(user_home);
+ size_t dinit_d_len = strlen("/dinit.d");
+ size_t full_len = user_home_len + dinit_d_len + 1;
+ char *service_dir_w = new char[full_len];
+ std::memcpy(service_dir_w, user_home, user_home_len);
+ std::memcpy(service_dir_w + user_home_len, "/dinit.d", dinit_d_len);
+ service_dir_w[full_len - 1] = 0;
+
+ service_dir = service_dir_w;
+ service_dir_dynamic = true;
}
}
- if (service_dir == nullptr) {
- service_dir = "/etc/dinit.d";
- }
-
if (services_to_start.empty()) {
services_to_start.push_back("boot");
}
log_flush_timer.add_timer(event_loop, dasynq::clock_type::MONOTONIC);
+ bool add_all_service_dirs = false;
+ if (service_dir == nullptr) {
+ service_dir = "/etc/dinit.d";
+ add_all_service_dirs = true;
+ }
+
/* start requested services */
- services = new dirload_service_set(service_dir);
+ services = new dirload_service_set(service_dir, service_dir_dynamic);
+ if (add_all_service_dirs) {
+ services->add_service_dir("/usr/local/lib/dinit.d", false);
+ services->add_service_dir("/lib/dinit.d", false);
+ }
- init_log(services);
+ init_log(services, log_is_syslog);
if (am_system_init) {
log(loglevel_t::INFO, false, "starting system");
}
+ // Only try to set up the external log now if we aren't the system init. (If we are the
+ // system init, wait until the log service starts).
+ if (! am_system_init) setup_external_log();
+
if (env_file != nullptr) {
read_env_file(env_file);
}
}
// Fork and execute dinit-reboot.
- execl("/sbin/shutdown", "/sbin/shutdown", "--system", cmd_arg, nullptr);
- log(loglevel_t::ERROR, "Could not execute /sbin/shutdown: ", strerror(errno));
+ constexpr auto shutdown_exec = literal(SBINDIR) + "/shutdown";
+ execl(shutdown_exec.c_str(), shutdown_exec.c_str(), "--system", cmd_arg, nullptr);
+ log(loglevel_t::ERROR, (literal("Could not execute ") + SBINDIR + "/shutdown: ").c_str(),
+ strerror(errno));
// PID 1 must not actually exit, although we should never reach this point:
while (true) {
void setup_external_log() noexcept
{
if (! external_log_open) {
-
- const char * saddrname = log_socket_path;
- size_t saddrname_len = strlen(saddrname);
- uint sockaddr_size = offsetof(struct sockaddr_un, sun_path) + saddrname_len + 1;
-
- struct sockaddr_un * name = static_cast<sockaddr_un *>(malloc(sockaddr_size));
- if (name == nullptr) {
- log(loglevel_t::ERROR, "Connecting to log socket: out of memory");
- return;
- }
-
- name->sun_family = AF_UNIX;
- memcpy(name->sun_path, saddrname, saddrname_len + 1);
-
- int sockfd = dinit_socket(AF_UNIX, SOCK_DGRAM, 0, SOCK_NONBLOCK | SOCK_CLOEXEC);
- if (sockfd == -1) {
- log(loglevel_t::ERROR, "Error creating log socket: ", strerror(errno));
- free(name);
- return;
- }
-
- if (connect(sockfd, (struct sockaddr *) name, sockaddr_size) == 0 || errno == EINPROGRESS) {
- // For EINPROGRESS, connection is still being established; however, we can select on
- // the file descriptor so we will be notified when it's ready. In other words we can
- // basically use it anyway.
- try {
- setup_main_log(sockfd);
+ if (log_is_syslog) {
+ const char * saddrname = log_path;
+ size_t saddrname_len = strlen(saddrname);
+ uint sockaddr_size = offsetof(struct sockaddr_un, sun_path) + saddrname_len + 1;
+
+ struct sockaddr_un * name = static_cast<sockaddr_un *>(malloc(sockaddr_size));
+ if (name == nullptr) {
+ log(loglevel_t::ERROR, "Connecting to log socket: out of memory");
+ return;
}
- catch (std::exception &e) {
- log(loglevel_t::ERROR, "Setting up log failed: ", e.what());
+
+ name->sun_family = AF_UNIX;
+ memcpy(name->sun_path, saddrname, saddrname_len + 1);
+
+ int sockfd = dinit_socket(AF_UNIX, SOCK_DGRAM, 0, SOCK_NONBLOCK | SOCK_CLOEXEC);
+ if (sockfd == -1) {
+ log(loglevel_t::ERROR, "Error creating log socket: ", strerror(errno));
+ free(name);
+ return;
+ }
+
+ if (connect(sockfd, (struct sockaddr *) name, sockaddr_size) == 0 || errno == EINPROGRESS) {
+ // For EINPROGRESS, connection is still being established; however, we can select on
+ // the file descriptor so we will be notified when it's ready. In other words we can
+ // basically use it anyway.
+ try {
+ setup_main_log(sockfd);
+ external_log_open = true;
+ }
+ catch (std::exception &e) {
+ log(loglevel_t::ERROR, "Setting up log failed: ", e.what());
+ close(sockfd);
+ }
+ }
+ else {
+ // Note if connect fails, we haven't warned at all, because the syslog server might not
+ // have started yet.
close(sockfd);
}
+
+ free(name);
}
else {
- // Note if connect fails, we haven't warned at all, because the syslog server might not
- // have started yet.
- close(sockfd);
+ // log to file:
+ int log_fd = open(log_path, O_WRONLY | O_CREAT | O_APPEND | O_NONBLOCK | O_CLOEXEC, 0644);
+ if (log_fd >= 0) {
+ try {
+ setup_main_log(log_fd);
+ external_log_open = true;
+ }
+ catch (std::exception &e) {
+ log(loglevel_t::ERROR, "Setting up log failed: ", e.what());
+ close(log_fd);
+ }
+ }
+ else {
+ // log failure to log? It makes more sense than first appears, because we also log to console:
+ log(loglevel_t::ERROR, "Setting up log failed: ", strerror(errno));
+ }
}
-
- free(name);
}
}
{
// This performs an immediate shutdown, without service rollback.
close_control_socket();
- execl("/sbin/shutdown", "/sbin/shutdown", "--system", (char *) 0);
- log(loglevel_t::ERROR, "Error executing /sbin/shutdown: ", strerror(errno));
+ constexpr auto shutdown_exec = literal(SBINDIR) + "/shutdown";
+ execl(shutdown_exec.c_str(), shutdown_exec.c_str(), "--system", (char *) 0);
+ log(loglevel_t::ERROR, literal("Error executing ") + SBINDIR + "/sbin/shutdown: ", strerror(errno));
sync(); // since a hard poweroff might be required at this point...
}