+* When a PROCESS service process dies, and smooth_recovery is false, probably
+ need to force-stop dependents even if the process itself was stopped
+ deliberately.
* Complete control socket handling and protocol
- support for pinned-start and pinned-stop
- support for listing all services
(eg there's no need to mount filesystems twice; there might be various other
system initialisations that can't or shouldn't really be "undone" and so do
not need to be re-done).
-* Add command line arg to start in "PID 1" mode (even if PID != 1).
- Basically, allow running as a system service monitor, without
- requiring that dinit runs as PID 1.
-* if PID != 1, choose a more sensible service definition directory
- (something like $HOME/dinit.d)
* Documentation including sample service definitions
* Better error handling, logging of errors (largely done, still some patches
of code where it may be missing).
* Investigate using cn_proc netlink connector (cn_proc.h) to receive process
termination events even when running with PID != 1 (Linux only).
Also, there is the possibility of having a small, simple PID-1 init which
- sends terminated process IDs over a pipe to Dinit.
+ sends terminated process IDs over a pipe to Dinit. Finally, it may be possible
+ to run dinit (and subprocesses) in a new PID namespace (again linux-only).
* Allow logging tasks to memory (growing or circular buffer) and later
switching to disk logging (allows for filesystem mounted readonly on boot)
* Rate control on process respawn
static bool control_socket_open = false;
int active_control_conns = 0;
+// 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 std::string control_socket_str;
+static const char *user_home_path = nullptr;
-int main(int argc, char **argv)
+
+// Get user home (and set user_home_path). (The return may become invalid after
+// changing the evironment (HOME variable) or using the getpwuid() function).
+const char * get_user_home()
{
- using namespace std;
-
- am_system_init = (getpid() == 1);
-
- if (am_system_init) {
- // setup STDIN, STDOUT, STDERR so that we can use them
- int onefd = open("/dev/console", O_RDONLY, 0);
- dup2(onefd, 0);
- int twofd = open("/dev/console", O_RDWR, 0);
- dup2(twofd, 1);
- dup2(twofd, 2);
- }
-
- /* Set up signal handlers etc */
- /* SIG_CHILD is ignored by default: good */
- /* sigemptyset(&sigwait_set); */
- /* sigaddset(&sigwait_set, SIGCHLD); */
- /* sigaddset(&sigwait_set, SIGINT); */
- /* sigaddset(&sigwait_set, SIGTERM); */
- /* sigprocmask(SIG_BLOCK, &sigwait_set, NULL); */
-
- // Terminal access control signals - we block these so that dinit can't be
- // suspended if it writes to the terminal after some other process has claimed
- // ownership of it.
- signal(SIGTSTP, SIG_IGN);
- signal(SIGTTIN, SIG_IGN);
- signal(SIGTTOU, SIG_IGN);
-
- /* list of services to start */
- list<const char *> services_to_start;
-
- if (! am_system_init) {
- char * userhome = getenv("HOME");
- if (userhome == nullptr) {
+ if (user_home_path == nullptr) {
+ user_home_path = getenv("HOME");
+ if (user_home_path == nullptr) {
struct passwd * pwuid_p = getpwuid(getuid());
if (pwuid_p != nullptr) {
- userhome = pwuid_p->pw_dir;
+ user_home_path = pwuid_p->pw_dir;
}
}
-
- if (userhome != nullptr) {
- control_socket_str = userhome;
- control_socket_str += "/.dinitctl";
- control_socket_path = control_socket_str.c_str();
- }
}
+ return user_home_path;
+}
+
+
+int main(int argc, char **argv)
+{
+ using namespace std;
- /* service directory name */
- const char * service_dir = "/etc/dinit.d";
+ am_system_init = (getpid() == 1);
+ const char * service_dir = nullptr;
+ string service_dir_str; // to hold storage for above if necessary
+ bool control_socket_path_set = false;
+
+ // list of services to start
+ list<const char *> services_to_start;
// Arguments, if given, specify a list of services to start.
// If we are running as init (PID=1), the kernel gives us any command line
// An option...
if (strcmp(argv[i], "--services-dir") == 0 ||
strcmp(argv[i], "-d") == 0) {
- ++i;
- if (i < argc) {
+ if (++i < argc) {
service_dir = argv[i];
}
else {
return 1;
}
}
+ else if (strcmp(argv[i], "--system") == 0 ||
+ strcmp(argv[i], "-s") == 0) {
+ am_system_init = true;
+ }
+ else if (strcmp(argv[i], "--socket-path") == 0 ||
+ strcmp(argv[i], "-p") == 0) {
+ if (++i < argc) {
+ control_socket_path = argv[i];
+ control_socket_path_set = true;
+ }
+ else {
+ cerr << "dinit: '--socket-path' (-p) 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;
- cout << " --services-dir <dir>, -d <dir> : set base directory for service description files (-d <dir>)" << endl;
- cout << " <service-name> : start service with name <service-name>" << endl;
+ cout << " --help display help" << endl;
+ cout << " --services-dir <dir>, -d <dir>" << endl;
+ cout << " set base directory for service description" << endl;
+ cout << " files (-d <dir>)" << endl;
+ cout << " --system, -s run as the system init process" << endl;
+ cout << " --socket-path <path>, -p <path>" << endl;
+ cout << " path to control socket" << endl;
+ cout << " <service-name> start service with name <service-name>" << endl;
return 0;
}
else {
}
}
+ if (am_system_init) {
+ // setup STDIN, STDOUT, STDERR so that we can use them
+ int onefd = open("/dev/console", O_RDONLY, 0);
+ dup2(onefd, 0);
+ int twofd = open("/dev/console", O_RDWR, 0);
+ dup2(twofd, 1);
+ dup2(twofd, 2);
+
+ if (onefd > 2) close(onefd);
+ if (twofd > 2) close(twofd);
+ }
+
+ /* Set up signal handlers etc */
+ /* SIG_CHILD is ignored by default: good */
+ /* sigemptyset(&sigwait_set); */
+ /* sigaddset(&sigwait_set, SIGCHLD); */
+ /* sigaddset(&sigwait_set, SIGINT); */
+ /* sigaddset(&sigwait_set, SIGTERM); */
+ /* sigprocmask(SIG_BLOCK, &sigwait_set, NULL); */
+
+ // Terminal access control signals - we block these so that dinit can't be
+ // suspended if it writes to the terminal after some other process has claimed
+ // ownership of it.
+ signal(SIGTSTP, SIG_IGN);
+ signal(SIGTTIN, SIG_IGN);
+ signal(SIGTTOU, SIG_IGN);
+
+ if (! am_system_init && ! control_socket_path_set) {
+ const char * userhome = get_user_home();
+ if (userhome != nullptr) {
+ control_socket_str = userhome;
+ control_socket_str += "/.dinitctl";
+ control_socket_path = control_socket_str.c_str();
+ }
+ }
+
+ /* service directory name */
+ 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();
+ }
+ }
+
+ if (service_dir == nullptr) {
+ service_dir = "/etc/dinit.d";
+ }
+
if (services_to_start.empty()) {
services_to_start.push_back("boot");
}
name->sun_family = AF_UNIX;
strcpy(name->sun_path, saddrname);
+ // OpenBSD and Linux both allow combining NONBLOCK/CLOEXEC flags with socket type, however
+ // it's not actually POSIX. (TODO).
int sockfd = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
if (sockfd == -1) {
log(LogLevel::ERROR, "Error creating control socket: ", strerror(errno));