manager.c revision f0469b8c4abbeee9ca69678245cd08314adc24c0
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering/***
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering This file is part of systemd.
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering Copyright 2010 Lennart Poettering
2f6a59070559786428d9eaf199ae3d61772b2225Kay Sievers
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering systemd is free software; you can redistribute it and/or modify it
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering under the terms of the GNU Lesser General Public License as published by
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering the Free Software Foundation; either version 2.1 of the License, or
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering (at your option) any later version.
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering systemd is distributed in the hope that it will be useful, but
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering WITHOUT ANY WARRANTY; without even the implied warranty of
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering Lesser General Public License for more details.
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering You should have received a copy of the GNU Lesser General Public License
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering along with systemd; If not, see <http://www.gnu.org/licenses/>.
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering***/
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering#include <dirent.h>
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering#include <errno.h>
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering#include <fcntl.h>
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering#include <linux/kd.h>
a9cdc94f7ff40f22a3cf9472f612a80730a1b010Dave Reisner#include <signal.h>
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering#include <string.h>
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen#include <sys/epoll.h>
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen#include <sys/inotify.h>
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen#include <sys/ioctl.h>
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering#include <sys/reboot.h>
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering#include <sys/timerfd.h>
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering#include <sys/wait.h>
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering#include <unistd.h>
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering#ifdef HAVE_AUDIT
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering#include <libaudit.h>
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering#endif
e1636421f46db6d06fbd028ef20a3113fa3e11f8Lennart Poettering
7085053a437456ab87d726f3697002dd811fdf7aDaniel Wallace#include "sd-daemon.h"
e1636421f46db6d06fbd028ef20a3113fa3e11f8Lennart Poettering#include "sd-messages.h"
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering#include "alloc-util.h"
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering#include "audit-fd.h"
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering#include "boot-timestamps.h"
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering#include "bus-common-errors.h"
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering#include "bus-error.h"
1b12a7b5896f94bdf33b3a6661ebabd761ea6adcHarald Hoyer#include "bus-kernel.h"
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering#include "bus-util.h"
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering#include "dbus-job.h"
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering#include "dbus-manager.h"
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering#include "dbus-unit.h"
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering#include "dbus.h"
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering#include "env-util.h"
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering#include "escape.h"
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering#include "exit-status.h"
46e65dcc3a522b5e992e165b5e61d14254026859Lennart Poettering#include "fd-util.h"
46e65dcc3a522b5e992e165b5e61d14254026859Lennart Poettering#include "fileio.h"
46e65dcc3a522b5e992e165b5e61d14254026859Lennart Poettering#include "fs-util.h"
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering#include "hashmap.h"
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering#include "io-util.h"
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering#include "locale-setup.h"
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering#include "log.h"
2f6a59070559786428d9eaf199ae3d61772b2225Kay Sievers#include "macro.h"
ffc06c3513d9a0693c7f810d03b20705127ba55aKay Sievers#include "manager.h"
2f6a59070559786428d9eaf199ae3d61772b2225Kay Sievers#include "missing.h"
2f6a59070559786428d9eaf199ae3d61772b2225Kay Sievers#include "mkdir.h"
2f6a59070559786428d9eaf199ae3d61772b2225Kay Sievers#include "parse-util.h"
2f6a59070559786428d9eaf199ae3d61772b2225Kay Sievers#include "path-lookup.h"
2f6a59070559786428d9eaf199ae3d61772b2225Kay Sievers#include "path-util.h"
2f6a59070559786428d9eaf199ae3d61772b2225Kay Sievers#include "process-util.h"
2f6a59070559786428d9eaf199ae3d61772b2225Kay Sievers#include "ratelimit.h"
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering#include "rm-rf.h"
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering#include "signal-util.h"
ffc06c3513d9a0693c7f810d03b20705127ba55aKay Sievers#include "special.h"
f18ca9dcdeda247e208f7143e834fd2fb2070d80Kay Sievers#include "stat-util.h"
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering#include "string-table.h"
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering#include "string-util.h"
9ff09bcb86fb125768667aca9bc0b10b1745370aShawn Landden#include "strv.h"
d95a74ed1191bb09f5be57b0619d3d77708e019dLennart Poettering#include "terminal-util.h"
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering#include "time-util.h"
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering#include "transaction.h"
599659860c770058f2eb04d578c521c16e0b1853Lennart Poettering#include "umask-util.h"
599659860c770058f2eb04d578c521c16e0b1853Lennart Poettering#include "unit-name.h"
d95a74ed1191bb09f5be57b0619d3d77708e019dLennart Poettering#include "util.h"
d95a74ed1191bb09f5be57b0619d3d77708e019dLennart Poettering#include "virt.h"
d95a74ed1191bb09f5be57b0619d3d77708e019dLennart Poettering#include "watchdog.h"
d95a74ed1191bb09f5be57b0619d3d77708e019dLennart Poettering
2311eb2ff0c3ff80ec3645b02c97170c9a565454Kay Sievers#define NOTIFY_RCVBUF_SIZE (8*1024*1024)
d95a74ed1191bb09f5be57b0619d3d77708e019dLennart Poettering
d95a74ed1191bb09f5be57b0619d3d77708e019dLennart Poettering/* Initial delay and the interval for printing status messages about running jobs */
d95a74ed1191bb09f5be57b0619d3d77708e019dLennart Poettering#define JOBS_IN_PROGRESS_WAIT_USEC (5*USEC_PER_SEC)
d95a74ed1191bb09f5be57b0619d3d77708e019dLennart Poettering#define JOBS_IN_PROGRESS_PERIOD_USEC (USEC_PER_SEC / 3)
d95a74ed1191bb09f5be57b0619d3d77708e019dLennart Poettering#define JOBS_IN_PROGRESS_PERIOD_DIVISOR 3
3e5e74d5b7f6fcbeff7b6e4e06abd931aab14c48Shawn Landden
9ff09bcb86fb125768667aca9bc0b10b1745370aShawn Landdenstatic int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata);
9ff09bcb86fb125768667aca9bc0b10b1745370aShawn Landdenstatic int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata);
9ff09bcb86fb125768667aca9bc0b10b1745370aShawn Landdenstatic int manager_dispatch_time_change_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata);
d95a74ed1191bb09f5be57b0619d3d77708e019dLennart Poetteringstatic int manager_dispatch_idle_pipe_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata);
9ff09bcb86fb125768667aca9bc0b10b1745370aShawn Landdenstatic int manager_dispatch_jobs_in_progress(sd_event_source *source, usec_t usec, void *userdata);
9ff09bcb86fb125768667aca9bc0b10b1745370aShawn Landdenstatic int manager_dispatch_run_queue(sd_event_source *source, void *userdata);
9ff09bcb86fb125768667aca9bc0b10b1745370aShawn Landdenstatic int manager_run_generators(Manager *m);
d95a74ed1191bb09f5be57b0619d3d77708e019dLennart Poetteringstatic void manager_undo_generators(Manager *m);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
9ff09bcb86fb125768667aca9bc0b10b1745370aShawn Landdenstatic void manager_watch_jobs_in_progress(Manager *m) {
5ffa8c818120e35c89becd938d160235c069dd12Zbigniew Jędrzejewski-Szmek usec_t next;
5ffa8c818120e35c89becd938d160235c069dd12Zbigniew Jędrzejewski-Szmek int r;
5ffa8c818120e35c89becd938d160235c069dd12Zbigniew Jędrzejewski-Szmek
5ffa8c818120e35c89becd938d160235c069dd12Zbigniew Jędrzejewski-Szmek assert(m);
5ffa8c818120e35c89becd938d160235c069dd12Zbigniew Jędrzejewski-Szmek
9ff09bcb86fb125768667aca9bc0b10b1745370aShawn Landden if (m->jobs_in_progress_event_source)
9ff09bcb86fb125768667aca9bc0b10b1745370aShawn Landden return;
9ff09bcb86fb125768667aca9bc0b10b1745370aShawn Landden
9ff09bcb86fb125768667aca9bc0b10b1745370aShawn Landden next = now(CLOCK_MONOTONIC) + JOBS_IN_PROGRESS_WAIT_USEC;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering r = sd_event_add_time(
2f6a59070559786428d9eaf199ae3d61772b2225Kay Sievers m->event,
2f6a59070559786428d9eaf199ae3d61772b2225Kay Sievers &m->jobs_in_progress_event_source,
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering CLOCK_MONOTONIC,
d95a74ed1191bb09f5be57b0619d3d77708e019dLennart Poettering next, 0,
5ffa8c818120e35c89becd938d160235c069dd12Zbigniew Jędrzejewski-Szmek manager_dispatch_jobs_in_progress, m);
5ffa8c818120e35c89becd938d160235c069dd12Zbigniew Jędrzejewski-Szmek if (r < 0)
2f6a59070559786428d9eaf199ae3d61772b2225Kay Sievers return;
9ff09bcb86fb125768667aca9bc0b10b1745370aShawn Landden
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering (void) sd_event_source_set_description(m->jobs_in_progress_event_source, "manager-jobs-in-progress");
5ffa8c818120e35c89becd938d160235c069dd12Zbigniew Jędrzejewski-Szmek}
5ffa8c818120e35c89becd938d160235c069dd12Zbigniew Jędrzejewski-Szmek
2667cc25896a15f82f9f1583e80d416beb1316e1Thomas Hindoe Paaboel Andersen#define CYLON_BUFFER_EXTRA (2*(sizeof(ANSI_RED)-1) + sizeof(ANSI_HIGHLIGHT_RED)-1 + 2*(sizeof(ANSI_NORMAL)-1))
d95a74ed1191bb09f5be57b0619d3d77708e019dLennart Poettering
d95a74ed1191bb09f5be57b0619d3d77708e019dLennart Poetteringstatic void draw_cylon(char buffer[], size_t buflen, unsigned width, unsigned pos) {
d95a74ed1191bb09f5be57b0619d3d77708e019dLennart Poettering char *p = buffer;
d95a74ed1191bb09f5be57b0619d3d77708e019dLennart Poettering
d95a74ed1191bb09f5be57b0619d3d77708e019dLennart Poettering assert(buflen >= CYLON_BUFFER_EXTRA + width + 1);
d95a74ed1191bb09f5be57b0619d3d77708e019dLennart Poettering assert(pos <= width+1); /* 0 or width+1 mean that the center light is behind the corner */
d95a74ed1191bb09f5be57b0619d3d77708e019dLennart Poettering
d95a74ed1191bb09f5be57b0619d3d77708e019dLennart Poettering if (pos > 1) {
d95a74ed1191bb09f5be57b0619d3d77708e019dLennart Poettering if (pos > 2)
d95a74ed1191bb09f5be57b0619d3d77708e019dLennart Poettering p = mempset(p, ' ', pos-2);
5ffa8c818120e35c89becd938d160235c069dd12Zbigniew Jędrzejewski-Szmek p = stpcpy(p, ANSI_RED);
b90930c73b1c82a3dc4d4f2603799993f042aaffLennart Poettering *p++ = '*';
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering }
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
5ffa8c818120e35c89becd938d160235c069dd12Zbigniew Jędrzejewski-Szmek if (pos > 0 && pos <= width) {
2f6a59070559786428d9eaf199ae3d61772b2225Kay Sievers p = stpcpy(p, ANSI_HIGHLIGHT_RED);
2f6a59070559786428d9eaf199ae3d61772b2225Kay Sievers *p++ = '*';
2f6a59070559786428d9eaf199ae3d61772b2225Kay Sievers }
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
2f6a59070559786428d9eaf199ae3d61772b2225Kay Sievers p = stpcpy(p, ANSI_NORMAL);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
c264aeab4b0e7b69f469e12e78d4a48b3ed7a66eKay Sievers if (pos < width) {
c264aeab4b0e7b69f469e12e78d4a48b3ed7a66eKay Sievers p = stpcpy(p, ANSI_RED);
c264aeab4b0e7b69f469e12e78d4a48b3ed7a66eKay Sievers *p++ = '*';
c264aeab4b0e7b69f469e12e78d4a48b3ed7a66eKay Sievers if (pos < width-1)
07a062a79374406e8f6b5a1e2f80c80baf031567Jason St. John p = mempset(p, ' ', width-1-pos);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering strcpy(p, ANSI_NORMAL);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering }
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen}
b92bea5d2a9481de69bb627a7b442a9f58fca43dZbigniew Jędrzejewski-Szmek
9f6eb1cd58f2ddf2eb6ba0e4de056e13d938af75Kay Sieversvoid manager_flip_auto_status(Manager *m, bool enable) {
9f6eb1cd58f2ddf2eb6ba0e4de056e13d938af75Kay Sievers assert(m);
9f6eb1cd58f2ddf2eb6ba0e4de056e13d938af75Kay Sievers
9f6eb1cd58f2ddf2eb6ba0e4de056e13d938af75Kay Sievers if (enable) {
9f6eb1cd58f2ddf2eb6ba0e4de056e13d938af75Kay Sievers if (m->show_status == SHOW_STATUS_AUTO)
9f6eb1cd58f2ddf2eb6ba0e4de056e13d938af75Kay Sievers manager_set_show_status(m, SHOW_STATUS_TEMPORARY);
9f6eb1cd58f2ddf2eb6ba0e4de056e13d938af75Kay Sievers } else {
9f6eb1cd58f2ddf2eb6ba0e4de056e13d938af75Kay Sievers if (m->show_status == SHOW_STATUS_TEMPORARY)
ffc06c3513d9a0693c7f810d03b20705127ba55aKay Sievers manager_set_show_status(m, SHOW_STATUS_AUTO);
ffc06c3513d9a0693c7f810d03b20705127ba55aKay Sievers }
ffc06c3513d9a0693c7f810d03b20705127ba55aKay Sievers}
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersenstatic void manager_print_jobs_in_progress(Manager *m) {
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering _cleanup_free_ char *job_of_n = NULL;
ffc06c3513d9a0693c7f810d03b20705127ba55aKay Sievers Iterator i;
ffc06c3513d9a0693c7f810d03b20705127ba55aKay Sievers Job *j;
ffc06c3513d9a0693c7f810d03b20705127ba55aKay Sievers unsigned counter = 0, print_nr;
9f6eb1cd58f2ddf2eb6ba0e4de056e13d938af75Kay Sievers char cylon[6 + CYLON_BUFFER_EXTRA + 1];
9f6eb1cd58f2ddf2eb6ba0e4de056e13d938af75Kay Sievers unsigned cylon_pos;
adacb9575a09981fcf11279f2f661e3fc21e58ffLennart Poettering char time[FORMAT_TIMESPAN_MAX], limit[FORMAT_TIMESPAN_MAX] = "no limit";
da927ba997d68401563b927f92e6e40e021a8e5cMichal Schmidt uint64_t x;
ffc06c3513d9a0693c7f810d03b20705127ba55aKay Sievers
adacb9575a09981fcf11279f2f661e3fc21e58ffLennart Poettering assert(m);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering assert(m->n_running_jobs > 0);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
ffc06c3513d9a0693c7f810d03b20705127ba55aKay Sievers manager_flip_auto_status(m, true);
ffc06c3513d9a0693c7f810d03b20705127ba55aKay Sievers
ffc06c3513d9a0693c7f810d03b20705127ba55aKay Sievers print_nr = (m->jobs_in_progress_iteration / JOBS_IN_PROGRESS_PERIOD_DIVISOR) % m->n_running_jobs;
ffc06c3513d9a0693c7f810d03b20705127ba55aKay Sievers
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering HASHMAP_FOREACH(j, m->jobs, i)
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering if (j->state == JOB_RUNNING && counter++ == print_nr)
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen break;
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen /* m->n_running_jobs must be consistent with the contents of m->jobs,
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering * so the above loop must have succeeded in finding j. */
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering assert(counter == print_nr + 1);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering assert(j);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering cylon_pos = m->jobs_in_progress_iteration % 14;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering if (cylon_pos >= 8)
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering cylon_pos = 14 - cylon_pos;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering draw_cylon(cylon, sizeof(cylon), 6, cylon_pos);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering m->jobs_in_progress_iteration++;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering if (m->n_running_jobs > 1) {
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering if (asprintf(&job_of_n, "(%u of %u) ", counter, m->n_running_jobs) < 0)
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering job_of_n = NULL;
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen }
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen format_timespan(time, sizeof(time), now(CLOCK_MONOTONIC) - j->begin_usec, 1*USEC_PER_SEC);
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen if (job_get_timeout(j, &x) > 0)
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen format_timespan(limit, sizeof(limit), x - j->begin_usec, 1*USEC_PER_SEC);
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen manager_status_printf(m, STATUS_TYPE_EPHEMERAL, cylon,
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen "%sA %s job is running for %s (%s / %s)",
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen strempty(job_of_n),
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen job_type_to_string(j->type),
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen unit_description(j->unit),
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen time, limit);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering}
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersenstatic int have_ask_password(void) {
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen _cleanup_closedir_ DIR *dir;
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering dir = opendir("/run/systemd/ask-password");
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering if (!dir) {
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering if (errno == ENOENT)
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering return false;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering else
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering return -errno;
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen }
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen for (;;) {
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen struct dirent *de;
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen errno = 0;
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen de = readdir(dir);
e5609878d8802e2469c433be418bcbcf55fbe63bLennart Poettering if (!de && errno > 0)
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen return -errno;
07a062a79374406e8f6b5a1e2f80c80baf031567Jason St. John if (!de)
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen return false;
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering if (startswith(de->d_name, "ask."))
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering return true;
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen }
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen}
e5609878d8802e2469c433be418bcbcf55fbe63bLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poetteringstatic int manager_dispatch_ask_password_fd(sd_event_source *source,
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering int fd, uint32_t revents, void *userdata) {
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering Manager *m = userdata;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering assert(m);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
e5609878d8802e2469c433be418bcbcf55fbe63bLennart Poettering flush_fd(fd);
e5609878d8802e2469c433be418bcbcf55fbe63bLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering m->have_ask_password = have_ask_password();
e5609878d8802e2469c433be418bcbcf55fbe63bLennart Poettering if (m->have_ask_password < 0)
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering /* Log error but continue. Negative have_ask_password
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering * is treated as unknown status. */
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen log_error_errno(m->have_ask_password, "Failed to list /run/systemd/ask-password: %m");
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen return 0;
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen}
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersenstatic void manager_close_ask_password(Manager *m) {
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen assert(m);
e5609878d8802e2469c433be418bcbcf55fbe63bLennart Poettering
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen m->ask_password_event_source = sd_event_source_unref(m->ask_password_event_source);
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen m->ask_password_inotify_fd = safe_close(m->ask_password_inotify_fd);
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen m->have_ask_password = -EINVAL;
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen}
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poetteringstatic int manager_check_ask_password(Manager *m) {
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen int r;
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen
e5609878d8802e2469c433be418bcbcf55fbe63bLennart Poettering assert(m);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering if (!m->ask_password_event_source) {
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering assert(m->ask_password_inotify_fd < 0);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering mkdir_p_label("/run/systemd/ask-password", 0755);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
e5609878d8802e2469c433be418bcbcf55fbe63bLennart Poettering m->ask_password_inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
e5609878d8802e2469c433be418bcbcf55fbe63bLennart Poettering if (m->ask_password_inotify_fd < 0)
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering return log_error_errno(errno, "inotify_init1() failed: %m");
e5609878d8802e2469c433be418bcbcf55fbe63bLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering if (inotify_add_watch(m->ask_password_inotify_fd, "/run/systemd/ask-password", IN_CREATE|IN_DELETE|IN_MOVE) < 0) {
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering log_error_errno(errno, "Failed to add watch on /run/systemd/ask-password: %m");
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen manager_close_ask_password(m);
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen return -errno;
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen }
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen r = sd_event_add_io(m->event, &m->ask_password_event_source,
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen m->ask_password_inotify_fd, EPOLLIN,
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen manager_dispatch_ask_password_fd, m);
e5609878d8802e2469c433be418bcbcf55fbe63bLennart Poettering if (r < 0) {
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen log_error_errno(errno, "Failed to add event source for /run/systemd/ask-password: %m");
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen manager_close_ask_password(m);
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen return -errno;
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen }
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering (void) sd_event_source_set_description(m->ask_password_event_source, "manager-ask-password");
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering /* Queries might have been added meanwhile... */
7568345034f2890af745747783c5abfbf6eccf0fLennart Poettering manager_dispatch_ask_password_fd(m->ask_password_event_source,
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering m->ask_password_inotify_fd, EPOLLIN, m);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering }
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering return m->have_ask_password;
7568345034f2890af745747783c5abfbf6eccf0fLennart Poettering}
f647962d64e844689f3e2acfce6102fc47e76df2Michal Schmidt
f647962d64e844689f3e2acfce6102fc47e76df2Michal Schmidtstatic int manager_watch_idle_pipe(Manager *m) {
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering int r;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
7c2d80944afb4196f2eff614e8da1450dffcbeaaThomas Hindoe Paaboel Andersen assert(m);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering if (m->idle_pipe_event_source)
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering return 0;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
601185b43da638b1c74153deae01dbd518680889Zbigniew Jędrzejewski-Szmek if (m->idle_pipe[2] < 0)
7591abd48079edc1f2adbd922e4b83eb73abeabeLennart Poettering return 0;
7591abd48079edc1f2adbd922e4b83eb73abeabeLennart Poettering
07a062a79374406e8f6b5a1e2f80c80baf031567Jason St. John r = sd_event_add_io(m->event, &m->idle_pipe_event_source, m->idle_pipe[2], EPOLLIN, manager_dispatch_idle_pipe_fd, m);
4f8f66cb4236783cd3cbee97fefc9aaa8469ac08Zbigniew Jędrzejewski-Szmek if (r < 0)
4f8f66cb4236783cd3cbee97fefc9aaa8469ac08Zbigniew Jędrzejewski-Szmek return log_error_errno(r, "Failed to watch idle pipe: %m");
4f8f66cb4236783cd3cbee97fefc9aaa8469ac08Zbigniew Jędrzejewski-Szmek
4f8f66cb4236783cd3cbee97fefc9aaa8469ac08Zbigniew Jędrzejewski-Szmek (void) sd_event_source_set_description(m->idle_pipe_event_source, "manager-idle-pipe");
4f8f66cb4236783cd3cbee97fefc9aaa8469ac08Zbigniew Jędrzejewski-Szmek
4f8f66cb4236783cd3cbee97fefc9aaa8469ac08Zbigniew Jędrzejewski-Szmek return 0;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering}
4f8f66cb4236783cd3cbee97fefc9aaa8469ac08Zbigniew Jędrzejewski-Szmek
4f8f66cb4236783cd3cbee97fefc9aaa8469ac08Zbigniew Jędrzejewski-Szmekstatic void manager_close_idle_pipe(Manager *m) {
07a062a79374406e8f6b5a1e2f80c80baf031567Jason St. John assert(m);
07a062a79374406e8f6b5a1e2f80c80baf031567Jason St. John
4f8f66cb4236783cd3cbee97fefc9aaa8469ac08Zbigniew Jędrzejewski-Szmek m->idle_pipe_event_source = sd_event_source_unref(m->idle_pipe_event_source);
3906ab4adf0aa7b952e39100262a11acd55cd79bRonny Chevalier
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering safe_close_pair(m->idle_pipe);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering safe_close_pair(m->idle_pipe + 2);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering}
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poetteringstatic int manager_setup_time_change(Manager *m) {
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering int r;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering /* We only care for the cancellation event, hence we set the
c978343015c787713651dff571acb5207367f5f2Lennart Poettering * timeout to the latest possible value. */
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering struct itimerspec its = {
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering .it_value.tv_sec = TIME_T_MAX,
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering };
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
c978343015c787713651dff571acb5207367f5f2Lennart Poettering assert(m);
c978343015c787713651dff571acb5207367f5f2Lennart Poettering assert_cc(sizeof(time_t) == sizeof(TIME_T_MAX));
c978343015c787713651dff571acb5207367f5f2Lennart Poettering
c978343015c787713651dff571acb5207367f5f2Lennart Poettering if (m->test_run)
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen return 0;
c978343015c787713651dff571acb5207367f5f2Lennart Poettering
c978343015c787713651dff571acb5207367f5f2Lennart Poettering /* Uses TFD_TIMER_CANCEL_ON_SET to get notifications whenever
eb9da376d76b48585b3b63b4f91903b54f7abd36Lennart Poettering * CLOCK_REALTIME makes a jump relative to CLOCK_MONOTONIC */
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering m->time_change_fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK|TFD_CLOEXEC);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering if (m->time_change_fd < 0)
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering return log_error_errno(errno, "Failed to create timerfd: %m");
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering if (timerfd_settime(m->time_change_fd, TFD_TIMER_ABSTIME|TFD_TIMER_CANCEL_ON_SET, &its, NULL) < 0) {
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering log_debug_errno(errno, "Failed to set up TFD_TIMER_CANCEL_ON_SET, ignoring: %m");
601185b43da638b1c74153deae01dbd518680889Zbigniew Jędrzejewski-Szmek m->time_change_fd = safe_close(m->time_change_fd);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering return 0;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering }
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering r = sd_event_add_io(m->event, &m->time_change_event_source, m->time_change_fd, EPOLLIN, manager_dispatch_time_change_fd, m);
601185b43da638b1c74153deae01dbd518680889Zbigniew Jędrzejewski-Szmek if (r < 0)
601185b43da638b1c74153deae01dbd518680889Zbigniew Jędrzejewski-Szmek return log_error_errno(r, "Failed to create time change event source: %m");
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering (void) sd_event_source_set_description(m->time_change_event_source, "manager-time-change");
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering log_debug("Set up TFD_TIMER_CANCEL_ON_SET timerfd.");
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering return 0;
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen}
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersenstatic int enable_special_signals(Manager *m) {
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering _cleanup_close_ int fd = -1;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen assert(m);
de33fc625725d199629ed074d6278504deb23debLennart Poettering
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen if (m->test_run)
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering return 0;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
546158bc6f46f8004cc11e81d19d223e0da56730Jan Janssen /* Enable that we get SIGINT on control-alt-del. In containers
546158bc6f46f8004cc11e81d19d223e0da56730Jan Janssen * this will fail with EPERM (older) or EINVAL (newer), so
546158bc6f46f8004cc11e81d19d223e0da56730Jan Janssen * ignore that. */
546158bc6f46f8004cc11e81d19d223e0da56730Jan Janssen if (reboot(RB_DISABLE_CAD) < 0 && errno != EPERM && errno != EINVAL)
c978343015c787713651dff571acb5207367f5f2Lennart Poettering log_warning_errno(errno, "Failed to enable ctrl-alt-del handling: %m");
c978343015c787713651dff571acb5207367f5f2Lennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering if (fd < 0) {
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering /* Support systems without virtual console */
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering if (fd != -ENOENT)
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering log_warning_errno(errno, "Failed to open /dev/tty0: %m");
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering } else {
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering /* Enable that we get SIGWINCH on kbrequest */
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering if (ioctl(fd, KDSIGACCEPT, SIGWINCH) < 0)
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering log_warning_errno(errno, "Failed to enable kbrequest handling: %m");
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering }
eb9da376d76b48585b3b63b4f91903b54f7abd36Lennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering return 0;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering}
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poetteringstatic int manager_setup_signals(Manager *m) {
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering struct sigaction sa = {
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen .sa_handler = SIG_DFL,
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering .sa_flags = SA_NOCLDSTOP|SA_RESTART,
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering };
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering sigset_t mask;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering int r;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering assert(m);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering assert_se(sigaction(SIGCHLD, &sa, NULL) == 0);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen /* We make liberal use of realtime signals here. On
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering * Linux/glibc we have 30 of them (with the exception of Linux
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering * on hppa, see below), between SIGRTMIN+0 ... SIGRTMIN+30
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering * (aka SIGRTMAX). */
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering assert_se(sigemptyset(&mask) == 0);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering sigset_add_many(&mask,
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering SIGCHLD, /* Child died */
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering SIGTERM, /* Reexecute daemon */
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering SIGHUP, /* Reload configuration */
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering SIGUSR1, /* systemd/upstart: reconnect to D-Bus */
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering SIGUSR2, /* systemd: dump status */
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering SIGINT, /* Kernel sends us this on control-alt-del */
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering SIGWINCH, /* Kernel sends us this on kbrequest (alt-arrowup) */
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering SIGPWR, /* Some kernel drivers and upsd send us this on power failure */
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering SIGRTMIN+0, /* systemd: start default.target */
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering SIGRTMIN+1, /* systemd: isolate rescue.target */
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering SIGRTMIN+2, /* systemd: isolate emergency.target */
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering SIGRTMIN+3, /* systemd: start halt.target */
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering SIGRTMIN+4, /* systemd: start poweroff.target */
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering SIGRTMIN+5, /* systemd: start reboot.target */
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering SIGRTMIN+6, /* systemd: start kexec.target */
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering /* ... space for more special targets ... */
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering SIGRTMIN+13, /* systemd: Immediate halt */
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering SIGRTMIN+14, /* systemd: Immediate poweroff */
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering SIGRTMIN+15, /* systemd: Immediate reboot */
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering SIGRTMIN+16, /* systemd: Immediate kexec */
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering /* ... space for more immediate system state changes ... */
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering SIGRTMIN+20, /* systemd: enable status messages */
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering SIGRTMIN+21, /* systemd: disable status messages */
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering SIGRTMIN+22, /* systemd: set log level to LOG_DEBUG */
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering SIGRTMIN+23, /* systemd: set log level to LOG_INFO */
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering SIGRTMIN+24, /* systemd: Immediate exit (--user only) */
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering /* .. one free signal here ... */
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering#if !defined(__hppa64__) && !defined(__hppa__)
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering /* Apparently Linux on hppa has fewer RT
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering * signals (SIGRTMAX is SIGRTMIN+25 there),
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering * hence let's not try to make use of them
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering * here. Since these commands are accessible
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering * by different means and only really a safety
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering * net, the missing functionality on hppa
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering * shouldn't matter. */
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering SIGRTMIN+26, /* systemd: set log target to journal-or-kmsg */
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering SIGRTMIN+27, /* systemd: set log target to console */
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering SIGRTMIN+28, /* systemd: set log target to kmsg */
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering SIGRTMIN+29, /* systemd: set log target to syslog-or-kmsg (obsolete) */
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering /* ... one free signal here SIGRTMIN+30 ... */
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering#endif
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering -1);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) == 0);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering m->signal_fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering if (m->signal_fd < 0)
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering return -errno;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering r = sd_event_add_io(m->event, &m->signal_event_source, m->signal_fd, EPOLLIN, manager_dispatch_signal_fd, m);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering if (r < 0)
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering return r;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering (void) sd_event_source_set_description(m->signal_event_source, "manager-signal");
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering /* Process signals a bit earlier than the rest of things, but
249968612f16a71df909d6e73785c18a9ff36a65Lennart Poettering * later than notify_fd processing, so that the notify
84f6181c2ac99a0514ca5e0c8fc8c8e284caf789Lennart Poettering * processing can still figure out to which process/service a
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering * message belongs, before we reap the process. */
a9cdc94f7ff40f22a3cf9472f612a80730a1b010Dave Reisner r = sd_event_source_set_priority(m->signal_event_source, SD_EVENT_PRIORITY_NORMAL-5);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering if (r < 0)
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering return r;
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering if (m->running_as == MANAGER_SYSTEM)
84f6181c2ac99a0514ca5e0c8fc8c8e284caf789Lennart Poettering return enable_special_signals(m);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering return 0;
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen}
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen
da927ba997d68401563b927f92e6e40e021a8e5cMichal Schmidtstatic void manager_clean_environment(Manager *m) {
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen assert(m);
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering /* Let's remove some environment variables that we
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen * need ourselves to communicate with our clients */
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering strv_env_unset_many(
a281d9c7851b16c4c9195d042901540ee9ced799Thomas Hindoe Paaboel Andersen m->environment,
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering "NOTIFY_SOCKET",
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering "MAINPID",
84f6181c2ac99a0514ca5e0c8fc8c8e284caf789Lennart Poettering "MANAGERPID",
6d0274f11547a0f11200bb82bf598a5a253e12cfLennart Poettering "LISTEN_PID",
"LISTEN_FDS",
"LISTEN_FDNAMES",
"WATCHDOG_PID",
"WATCHDOG_USEC",
NULL);
}
static int manager_default_environment(Manager *m) {
assert(m);
if (m->running_as == MANAGER_SYSTEM) {
/* The system manager always starts with a clean
* environment for its children. It does not import
* the kernel or the parents exported variables.
*
* The initial passed environ is untouched to keep
* /proc/self/environ valid; it is used for tagging
* the init process inside containers. */
m->environment = strv_new("PATH=" DEFAULT_PATH,
NULL);
/* Import locale variables LC_*= from configuration */
locale_setup(&m->environment);
} else {
/* The user manager passes its own environment
* along to its children. */
m->environment = strv_copy(environ);
}
if (!m->environment)
return -ENOMEM;
manager_clean_environment(m);
strv_sort(m->environment);
return 0;
}
int manager_new(ManagerRunningAs running_as, bool test_run, Manager **_m) {
static const char * const unit_log_fields[_MANAGER_RUNNING_AS_MAX] = {
[MANAGER_SYSTEM] = "UNIT=",
[MANAGER_USER] = "USER_UNIT=",
};
static const char * const unit_log_format_strings[_MANAGER_RUNNING_AS_MAX] = {
[MANAGER_SYSTEM] = "UNIT=%s",
[MANAGER_USER] = "USER_UNIT=%s",
};
Manager *m;
int r;
assert(_m);
assert(running_as >= 0);
assert(running_as < _MANAGER_RUNNING_AS_MAX);
m = new0(Manager, 1);
if (!m)
return -ENOMEM;
#ifdef ENABLE_EFI
if (running_as == MANAGER_SYSTEM && detect_container() <= 0)
boot_timestamps(&m->userspace_timestamp, &m->firmware_timestamp, &m->loader_timestamp);
#endif
m->running_as = running_as;
m->exit_code = _MANAGER_EXIT_CODE_INVALID;
m->default_timer_accuracy_usec = USEC_PER_MINUTE;
m->default_tasks_accounting = true;
m->default_tasks_max = UINT64_C(512);
/* Prepare log fields we can use for structured logging */
m->unit_log_field = unit_log_fields[running_as];
m->unit_log_format_string = unit_log_format_strings[running_as];
m->idle_pipe[0] = m->idle_pipe[1] = m->idle_pipe[2] = m->idle_pipe[3] = -1;
m->pin_cgroupfs_fd = m->notify_fd = m->signal_fd = m->time_change_fd =
m->dev_autofs_fd = m->private_listen_fd = m->kdbus_fd = m->cgroup_inotify_fd = -1;
m->current_job_id = 1; /* start as id #1, so that we can leave #0 around as "null-like" value */
m->ask_password_inotify_fd = -1;
m->have_ask_password = -EINVAL; /* we don't know */
m->first_boot = -1;
m->cgroup_netclass_registry_last = CGROUP_NETCLASS_FIXED_MAX;
m->test_run = test_run;
/* Reboot immediately if the user hits C-A-D more often than 7x per 2s */
RATELIMIT_INIT(m->ctrl_alt_del_ratelimit, 2 * USEC_PER_SEC, 7);
r = manager_default_environment(m);
if (r < 0)
goto fail;
r = hashmap_ensure_allocated(&m->units, &string_hash_ops);
if (r < 0)
goto fail;
r = hashmap_ensure_allocated(&m->jobs, NULL);
if (r < 0)
goto fail;
r = hashmap_ensure_allocated(&m->cgroup_unit, &string_hash_ops);
if (r < 0)
goto fail;
r = hashmap_ensure_allocated(&m->watch_bus, &string_hash_ops);
if (r < 0)
goto fail;
r = sd_event_default(&m->event);
if (r < 0)
goto fail;
r = sd_event_add_defer(m->event, &m->run_queue_event_source, manager_dispatch_run_queue, m);
if (r < 0)
goto fail;
r = sd_event_source_set_priority(m->run_queue_event_source, SD_EVENT_PRIORITY_IDLE);
if (r < 0)
goto fail;
r = sd_event_source_set_enabled(m->run_queue_event_source, SD_EVENT_OFF);
if (r < 0)
goto fail;
(void) sd_event_source_set_description(m->run_queue_event_source, "manager-run-queue");
r = manager_setup_signals(m);
if (r < 0)
goto fail;
r = manager_setup_cgroup(m);
if (r < 0)
goto fail;
r = manager_setup_time_change(m);
if (r < 0)
goto fail;
m->udev = udev_new();
if (!m->udev) {
r = -ENOMEM;
goto fail;
}
/* Note that we set up neither kdbus, nor the notify fd
* here. We do that after deserialization, since they might
* have gotten serialized across the reexec. */
m->taint_usr = dir_is_empty("/usr") > 0;
*_m = m;
return 0;
fail:
manager_free(m);
return r;
}
static int manager_setup_notify(Manager *m) {
int r;
if (m->test_run)
return 0;
if (m->notify_fd < 0) {
_cleanup_close_ int fd = -1;
union sockaddr_union sa = {
.sa.sa_family = AF_UNIX,
};
static const int one = 1;
/* First free all secondary fields */
m->notify_socket = mfree(m->notify_socket);
m->notify_event_source = sd_event_source_unref(m->notify_event_source);
fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
if (fd < 0)
return log_error_errno(errno, "Failed to allocate notification socket: %m");
fd_inc_rcvbuf(fd, NOTIFY_RCVBUF_SIZE);
if (m->running_as == MANAGER_SYSTEM)
m->notify_socket = strdup("/run/systemd/notify");
else {
const char *e;
e = getenv("XDG_RUNTIME_DIR");
if (!e) {
log_error_errno(errno, "XDG_RUNTIME_DIR is not set: %m");
return -EINVAL;
}
m->notify_socket = strappend(e, "/systemd/notify");
}
if (!m->notify_socket)
return log_oom();
(void) mkdir_parents_label(m->notify_socket, 0755);
(void) unlink(m->notify_socket);
strncpy(sa.un.sun_path, m->notify_socket, sizeof(sa.un.sun_path)-1);
r = bind(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path));
if (r < 0)
return log_error_errno(errno, "bind(%s) failed: %m", sa.un.sun_path);
r = setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one));
if (r < 0)
return log_error_errno(errno, "SO_PASSCRED failed: %m");
m->notify_fd = fd;
fd = -1;
log_debug("Using notification socket %s", m->notify_socket);
}
if (!m->notify_event_source) {
r = sd_event_add_io(m->event, &m->notify_event_source, m->notify_fd, EPOLLIN, manager_dispatch_notify_fd, m);
if (r < 0)
return log_error_errno(r, "Failed to allocate notify event source: %m");
/* Process signals a bit earlier than SIGCHLD, so that we can
* still identify to which service an exit message belongs */
r = sd_event_source_set_priority(m->notify_event_source, SD_EVENT_PRIORITY_NORMAL-7);
if (r < 0)
return log_error_errno(r, "Failed to set priority of notify event source: %m");
(void) sd_event_source_set_description(m->notify_event_source, "manager-notify");
}
return 0;
}
static int manager_setup_kdbus(Manager *m) {
_cleanup_free_ char *p = NULL;
assert(m);
if (m->test_run || m->kdbus_fd >= 0)
return 0;
if (!is_kdbus_available())
return -ESOCKTNOSUPPORT;
m->kdbus_fd = bus_kernel_create_bus(
m->running_as == MANAGER_SYSTEM ? "system" : "user",
m->running_as == MANAGER_SYSTEM, &p);
if (m->kdbus_fd < 0)
return log_debug_errno(m->kdbus_fd, "Failed to set up kdbus: %m");
log_debug("Successfully set up kdbus on %s", p);
return 0;
}
static int manager_connect_bus(Manager *m, bool reexecuting) {
bool try_bus_connect;
assert(m);
if (m->test_run)
return 0;
try_bus_connect =
m->kdbus_fd >= 0 ||
reexecuting ||
(m->running_as == MANAGER_USER && getenv("DBUS_SESSION_BUS_ADDRESS"));
/* Try to connect to the buses, if possible. */
return bus_init(m, try_bus_connect);
}
static unsigned manager_dispatch_cleanup_queue(Manager *m) {
Unit *u;
unsigned n = 0;
assert(m);
while ((u = m->cleanup_queue)) {
assert(u->in_cleanup_queue);
unit_free(u);
n++;
}
return n;
}
enum {
GC_OFFSET_IN_PATH, /* This one is on the path we were traveling */
GC_OFFSET_UNSURE, /* No clue */
GC_OFFSET_GOOD, /* We still need this unit */
GC_OFFSET_BAD, /* We don't need this unit anymore */
_GC_OFFSET_MAX
};
static void unit_gc_sweep(Unit *u, unsigned gc_marker) {
Iterator i;
Unit *other;
bool is_bad;
assert(u);
if (u->gc_marker == gc_marker + GC_OFFSET_GOOD ||
u->gc_marker == gc_marker + GC_OFFSET_BAD ||
u->gc_marker == gc_marker + GC_OFFSET_IN_PATH)
return;
if (u->in_cleanup_queue)
goto bad;
if (unit_check_gc(u))
goto good;
u->gc_marker = gc_marker + GC_OFFSET_IN_PATH;
is_bad = true;
SET_FOREACH(other, u->dependencies[UNIT_REFERENCED_BY], i) {
unit_gc_sweep(other, gc_marker);
if (other->gc_marker == gc_marker + GC_OFFSET_GOOD)
goto good;
if (other->gc_marker != gc_marker + GC_OFFSET_BAD)
is_bad = false;
}
if (is_bad)
goto bad;
/* We were unable to find anything out about this entry, so
* let's investigate it later */
u->gc_marker = gc_marker + GC_OFFSET_UNSURE;
unit_add_to_gc_queue(u);
return;
bad:
/* We definitely know that this one is not useful anymore, so
* let's mark it for deletion */
u->gc_marker = gc_marker + GC_OFFSET_BAD;
unit_add_to_cleanup_queue(u);
return;
good:
u->gc_marker = gc_marker + GC_OFFSET_GOOD;
}
static unsigned manager_dispatch_gc_queue(Manager *m) {
Unit *u;
unsigned n = 0;
unsigned gc_marker;
assert(m);
/* log_debug("Running GC..."); */
m->gc_marker += _GC_OFFSET_MAX;
if (m->gc_marker + _GC_OFFSET_MAX <= _GC_OFFSET_MAX)
m->gc_marker = 1;
gc_marker = m->gc_marker;
while ((u = m->gc_queue)) {
assert(u->in_gc_queue);
unit_gc_sweep(u, gc_marker);
LIST_REMOVE(gc_queue, m->gc_queue, u);
u->in_gc_queue = false;
n++;
if (u->gc_marker == gc_marker + GC_OFFSET_BAD ||
u->gc_marker == gc_marker + GC_OFFSET_UNSURE) {
if (u->id)
log_unit_debug(u, "Collecting.");
u->gc_marker = gc_marker + GC_OFFSET_BAD;
unit_add_to_cleanup_queue(u);
}
}
m->n_in_gc_queue = 0;
return n;
}
static void manager_clear_jobs_and_units(Manager *m) {
Unit *u;
assert(m);
while ((u = hashmap_first(m->units)))
unit_free(u);
manager_dispatch_cleanup_queue(m);
assert(!m->load_queue);
assert(!m->run_queue);
assert(!m->dbus_unit_queue);
assert(!m->dbus_job_queue);
assert(!m->cleanup_queue);
assert(!m->gc_queue);
assert(hashmap_isempty(m->jobs));
assert(hashmap_isempty(m->units));
m->n_on_console = 0;
m->n_running_jobs = 0;
}
Manager* manager_free(Manager *m) {
UnitType c;
int i;
if (!m)
return NULL;
manager_clear_jobs_and_units(m);
for (c = 0; c < _UNIT_TYPE_MAX; c++)
if (unit_vtable[c]->shutdown)
unit_vtable[c]->shutdown(m);
/* If we reexecute ourselves, we keep the root cgroup
* around */
manager_shutdown_cgroup(m, m->exit_code != MANAGER_REEXECUTE);
manager_undo_generators(m);
bus_done(m);
hashmap_free(m->units);
hashmap_free(m->jobs);
hashmap_free(m->watch_pids1);
hashmap_free(m->watch_pids2);
hashmap_free(m->watch_bus);
set_free(m->startup_units);
set_free(m->failed_units);
sd_event_source_unref(m->signal_event_source);
sd_event_source_unref(m->notify_event_source);
sd_event_source_unref(m->time_change_event_source);
sd_event_source_unref(m->jobs_in_progress_event_source);
sd_event_source_unref(m->run_queue_event_source);
safe_close(m->signal_fd);
safe_close(m->notify_fd);
safe_close(m->time_change_fd);
safe_close(m->kdbus_fd);
manager_close_ask_password(m);
manager_close_idle_pipe(m);
udev_unref(m->udev);
sd_event_unref(m->event);
free(m->notify_socket);
lookup_paths_free(&m->lookup_paths);
strv_free(m->environment);
hashmap_free(m->cgroup_unit);
set_free_free(m->unit_path_cache);
hashmap_free(m->cgroup_netclass_registry);
free(m->switch_root);
free(m->switch_root_init);
for (i = 0; i < _RLIMIT_MAX; i++)
m->rlimit[i] = mfree(m->rlimit[i]);
assert(hashmap_isempty(m->units_requiring_mounts_for));
hashmap_free(m->units_requiring_mounts_for);
free(m);
return NULL;
}
void manager_enumerate(Manager *m) {
UnitType c;
assert(m);
/* Let's ask every type to load all units from disk/kernel
* that it might know */
for (c = 0; c < _UNIT_TYPE_MAX; c++) {
if (!unit_type_supported(c)) {
log_debug("Unit type .%s is not supported on this system.", unit_type_to_string(c));
continue;
}
if (!unit_vtable[c]->enumerate)
continue;
unit_vtable[c]->enumerate(m);
}
manager_dispatch_load_queue(m);
}
static void manager_coldplug(Manager *m) {
Iterator i;
Unit *u;
char *k;
int r;
assert(m);
/* Then, let's set up their initial state. */
HASHMAP_FOREACH_KEY(u, k, m->units, i) {
/* ignore aliases */
if (u->id != k)
continue;
r = unit_coldplug(u);
if (r < 0)
log_warning_errno(r, "We couldn't coldplug %s, proceeding anyway: %m", u->id);
}
}
static void manager_build_unit_path_cache(Manager *m) {
char **i;
_cleanup_closedir_ DIR *d = NULL;
int r;
assert(m);
set_free_free(m->unit_path_cache);
m->unit_path_cache = set_new(&string_hash_ops);
if (!m->unit_path_cache) {
log_error("Failed to allocate unit path cache.");
return;
}
/* This simply builds a list of files we know exist, so that
* we don't always have to go to disk */
STRV_FOREACH(i, m->lookup_paths.unit_path) {
struct dirent *de;
d = opendir(*i);
if (!d) {
if (errno != ENOENT)
log_error_errno(errno, "Failed to open directory %s: %m", *i);
continue;
}
while ((de = readdir(d))) {
char *p;
if (hidden_file(de->d_name))
continue;
p = strjoin(streq(*i, "/") ? "" : *i, "/", de->d_name, NULL);
if (!p) {
r = -ENOMEM;
goto fail;
}
r = set_consume(m->unit_path_cache, p);
if (r < 0)
goto fail;
}
d = safe_closedir(d);
}
return;
fail:
log_error_errno(r, "Failed to build unit path cache: %m");
set_free_free(m->unit_path_cache);
m->unit_path_cache = NULL;
}
static void manager_distribute_fds(Manager *m, FDSet *fds) {
Iterator i;
Unit *u;
assert(m);
HASHMAP_FOREACH(u, m->units, i) {
if (fdset_size(fds) <= 0)
break;
if (!UNIT_VTABLE(u)->distribute_fds)
continue;
UNIT_VTABLE(u)->distribute_fds(u, fds);
}
}
int manager_startup(Manager *m, FILE *serialization, FDSet *fds) {
int r, q;
assert(m);
dual_timestamp_get(&m->generators_start_timestamp);
r = manager_run_generators(m);
dual_timestamp_get(&m->generators_finish_timestamp);
if (r < 0)
return r;
r = lookup_paths_init(
&m->lookup_paths, m->running_as, true,
NULL,
m->generator_unit_path,
m->generator_unit_path_early,
m->generator_unit_path_late);
if (r < 0)
return r;
manager_build_unit_path_cache(m);
/* If we will deserialize make sure that during enumeration
* this is already known, so we increase the counter here
* already */
if (serialization)
m->n_reloading ++;
/* First, enumerate what we can from all config files */
dual_timestamp_get(&m->units_load_start_timestamp);
manager_enumerate(m);
dual_timestamp_get(&m->units_load_finish_timestamp);
/* Second, deserialize if there is something to deserialize */
if (serialization)
r = manager_deserialize(m, serialization, fds);
/* Any fds left? Find some unit which wants them. This is
* useful to allow container managers to pass some file
* descriptors to us pre-initialized. This enables
* socket-based activation of entire containers. */
manager_distribute_fds(m, fds);
/* We might have deserialized the notify fd, but if we didn't
* then let's create the bus now */
q = manager_setup_notify(m);
if (q < 0 && r == 0)
r = q;
/* We might have deserialized the kdbus control fd, but if we
* didn't, then let's create the bus now. */
manager_setup_kdbus(m);
manager_connect_bus(m, !!serialization);
bus_track_coldplug(m, &m->subscribed, &m->deserialized_subscribed);
/* Third, fire things up! */
manager_coldplug(m);
if (serialization) {
assert(m->n_reloading > 0);
m->n_reloading --;
/* Let's wait for the UnitNew/JobNew messages being
* sent, before we notify that the reload is
* finished */
m->send_reloading_done = true;
}
return r;
}
int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, sd_bus_error *e, Job **_ret) {
int r;
Transaction *tr;
assert(m);
assert(type < _JOB_TYPE_MAX);
assert(unit);
assert(mode < _JOB_MODE_MAX);
if (mode == JOB_ISOLATE && type != JOB_START)
return sd_bus_error_setf(e, SD_BUS_ERROR_INVALID_ARGS, "Isolate is only valid for start.");
if (mode == JOB_ISOLATE && !unit->allow_isolate)
return sd_bus_error_setf(e, BUS_ERROR_NO_ISOLATION, "Operation refused, unit may not be isolated.");
log_unit_debug(unit, "Trying to enqueue job %s/%s/%s", unit->id, job_type_to_string(type), job_mode_to_string(mode));
type = job_type_collapse(type, unit);
tr = transaction_new(mode == JOB_REPLACE_IRREVERSIBLY);
if (!tr)
return -ENOMEM;
r = transaction_add_job_and_dependencies(tr, type, unit, NULL, true, false,
mode == JOB_IGNORE_DEPENDENCIES || mode == JOB_IGNORE_REQUIREMENTS,
mode == JOB_IGNORE_DEPENDENCIES, e);
if (r < 0)
goto tr_abort;
if (mode == JOB_ISOLATE) {
r = transaction_add_isolate_jobs(tr, m);
if (r < 0)
goto tr_abort;
}
r = transaction_activate(tr, m, mode, e);
if (r < 0)
goto tr_abort;
log_unit_debug(unit,
"Enqueued job %s/%s as %u", unit->id,
job_type_to_string(type), (unsigned) tr->anchor_job->id);
if (_ret)
*_ret = tr->anchor_job;
transaction_free(tr);
return 0;
tr_abort:
transaction_abort(tr);
transaction_free(tr);
return r;
}
int manager_add_job_by_name(Manager *m, JobType type, const char *name, JobMode mode, sd_bus_error *e, Job **ret) {
Unit *unit;
int r;
assert(m);
assert(type < _JOB_TYPE_MAX);
assert(name);
assert(mode < _JOB_MODE_MAX);
r = manager_load_unit(m, name, NULL, NULL, &unit);
if (r < 0)
return r;
return manager_add_job(m, type, unit, mode, e, ret);
}
int manager_add_job_by_name_and_warn(Manager *m, JobType type, const char *name, JobMode mode, Job **ret) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
int r;
assert(m);
assert(type < _JOB_TYPE_MAX);
assert(name);
assert(mode < _JOB_MODE_MAX);
r = manager_add_job_by_name(m, type, name, mode, &error, ret);
if (r < 0)
return log_warning_errno(r, "Failed to enqueue %s job for %s: %s", job_mode_to_string(mode), name, bus_error_message(&error, r));
return r;
}
Job *manager_get_job(Manager *m, uint32_t id) {
assert(m);
return hashmap_get(m->jobs, UINT32_TO_PTR(id));
}
Unit *manager_get_unit(Manager *m, const char *name) {
assert(m);
assert(name);
return hashmap_get(m->units, name);
}
unsigned manager_dispatch_load_queue(Manager *m) {
Unit *u;
unsigned n = 0;
assert(m);
/* Make sure we are not run recursively */
if (m->dispatching_load_queue)
return 0;
m->dispatching_load_queue = true;
/* Dispatches the load queue. Takes a unit from the queue and
* tries to load its data until the queue is empty */
while ((u = m->load_queue)) {
assert(u->in_load_queue);
unit_load(u);
n++;
}
m->dispatching_load_queue = false;
return n;
}
int manager_load_unit_prepare(
Manager *m,
const char *name,
const char *path,
sd_bus_error *e,
Unit **_ret) {
Unit *ret;
UnitType t;
int r;
assert(m);
assert(name || path);
/* This will prepare the unit for loading, but not actually
* load anything from disk. */
if (path && !is_path(path))
return sd_bus_error_setf(e, SD_BUS_ERROR_INVALID_ARGS, "Path %s is not absolute.", path);
if (!name)
name = basename(path);
t = unit_name_to_type(name);
if (t == _UNIT_TYPE_INVALID || !unit_name_is_valid(name, UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE))
return sd_bus_error_setf(e, SD_BUS_ERROR_INVALID_ARGS, "Unit name %s is not valid.", name);
ret = manager_get_unit(m, name);
if (ret) {
*_ret = ret;
return 1;
}
ret = unit_new(m, unit_vtable[t]->object_size);
if (!ret)
return -ENOMEM;
if (path) {
ret->fragment_path = strdup(path);
if (!ret->fragment_path) {
unit_free(ret);
return -ENOMEM;
}
}
r = unit_add_name(ret, name);
if (r < 0) {
unit_free(ret);
return r;
}
unit_add_to_load_queue(ret);
unit_add_to_dbus_queue(ret);
unit_add_to_gc_queue(ret);
if (_ret)
*_ret = ret;
return 0;
}
int manager_load_unit(
Manager *m,
const char *name,
const char *path,
sd_bus_error *e,
Unit **_ret) {
int r;
assert(m);
/* This will load the service information files, but not actually
* start any services or anything. */
r = manager_load_unit_prepare(m, name, path, e, _ret);
if (r != 0)
return r;
manager_dispatch_load_queue(m);
if (_ret)
*_ret = unit_follow_merge(*_ret);
return 0;
}
void manager_dump_jobs(Manager *s, FILE *f, const char *prefix) {
Iterator i;
Job *j;
assert(s);
assert(f);
HASHMAP_FOREACH(j, s->jobs, i)
job_dump(j, f, prefix);
}
void manager_dump_units(Manager *s, FILE *f, const char *prefix) {
Iterator i;
Unit *u;
const char *t;
assert(s);
assert(f);
HASHMAP_FOREACH_KEY(u, t, s->units, i)
if (u->id == t)
unit_dump(u, f, prefix);
}
void manager_clear_jobs(Manager *m) {
Job *j;
assert(m);
while ((j = hashmap_first(m->jobs)))
/* No need to recurse. We're cancelling all jobs. */
job_finish_and_invalidate(j, JOB_CANCELED, false);
}
static int manager_dispatch_run_queue(sd_event_source *source, void *userdata) {
Manager *m = userdata;
Job *j;
assert(source);
assert(m);
while ((j = m->run_queue)) {
assert(j->installed);
assert(j->in_run_queue);
job_run_and_invalidate(j);
}
if (m->n_running_jobs > 0)
manager_watch_jobs_in_progress(m);
if (m->n_on_console > 0)
manager_watch_idle_pipe(m);
return 1;
}
static unsigned manager_dispatch_dbus_queue(Manager *m) {
Job *j;
Unit *u;
unsigned n = 0;
assert(m);
if (m->dispatching_dbus_queue)
return 0;
m->dispatching_dbus_queue = true;
while ((u = m->dbus_unit_queue)) {
assert(u->in_dbus_queue);
bus_unit_send_change_signal(u);
n++;
}
while ((j = m->dbus_job_queue)) {
assert(j->in_dbus_queue);
bus_job_send_change_signal(j);
n++;
}
m->dispatching_dbus_queue = false;
if (m->send_reloading_done) {
m->send_reloading_done = false;
bus_manager_send_reloading(m, false);
}
if (m->queued_message)
bus_send_queued_message(m);
return n;
}
static void manager_invoke_notify_message(Manager *m, Unit *u, pid_t pid, const char *buf, size_t n, FDSet *fds) {
_cleanup_strv_free_ char **tags = NULL;
assert(m);
assert(u);
assert(buf);
assert(n > 0);
tags = strv_split(buf, "\n\r");
if (!tags) {
log_oom();
return;
}
if (UNIT_VTABLE(u)->notify_message)
UNIT_VTABLE(u)->notify_message(u, pid, tags, fds);
else
log_unit_debug(u, "Got notification message for unit. Ignoring.");
}
static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
_cleanup_fdset_free_ FDSet *fds = NULL;
Manager *m = userdata;
char buf[NOTIFY_BUFFER_MAX+1];
struct iovec iovec = {
.iov_base = buf,
.iov_len = sizeof(buf)-1,
};
union {
struct cmsghdr cmsghdr;
uint8_t buf[CMSG_SPACE(sizeof(struct ucred)) +
CMSG_SPACE(sizeof(int) * NOTIFY_FD_MAX)];
} control = {};
struct msghdr msghdr = {
.msg_iov = &iovec,
.msg_iovlen = 1,
.msg_control = &control,
.msg_controllen = sizeof(control),
};
struct cmsghdr *cmsg;
struct ucred *ucred = NULL;
bool found = false;
Unit *u1, *u2, *u3;
int r, *fd_array = NULL;
unsigned n_fds = 0;
ssize_t n;
assert(m);
assert(m->notify_fd == fd);
if (revents != EPOLLIN) {
log_warning("Got unexpected poll event for notify fd.");
return 0;
}
n = recvmsg(m->notify_fd, &msghdr, MSG_DONTWAIT|MSG_CMSG_CLOEXEC);
if (n < 0) {
if (errno == EAGAIN || errno == EINTR)
return 0;
return -errno;
}
CMSG_FOREACH(cmsg, &msghdr) {
if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
fd_array = (int*) CMSG_DATA(cmsg);
n_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
} else if (cmsg->cmsg_level == SOL_SOCKET &&
cmsg->cmsg_type == SCM_CREDENTIALS &&
cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred))) {
ucred = (struct ucred*) CMSG_DATA(cmsg);
}
}
if (n_fds > 0) {
assert(fd_array);
r = fdset_new_array(&fds, fd_array, n_fds);
if (r < 0) {
close_many(fd_array, n_fds);
return log_oom();
}
}
if (!ucred || ucred->pid <= 0) {
log_warning("Received notify message without valid credentials. Ignoring.");
return 0;
}
if ((size_t) n >= sizeof(buf)) {
log_warning("Received notify message exceeded maximum size. Ignoring.");
return 0;
}
buf[n] = 0;
/* Notify every unit that might be interested, but try
* to avoid notifying the same one multiple times. */
u1 = manager_get_unit_by_pid_cgroup(m, ucred->pid);
if (u1) {
manager_invoke_notify_message(m, u1, ucred->pid, buf, n, fds);
found = true;
}
u2 = hashmap_get(m->watch_pids1, PID_TO_PTR(ucred->pid));
if (u2 && u2 != u1) {
manager_invoke_notify_message(m, u2, ucred->pid, buf, n, fds);
found = true;
}
u3 = hashmap_get(m->watch_pids2, PID_TO_PTR(ucred->pid));
if (u3 && u3 != u2 && u3 != u1) {
manager_invoke_notify_message(m, u3, ucred->pid, buf, n, fds);
found = true;
}
if (!found)
log_warning("Cannot find unit for notify message of PID "PID_FMT".", ucred->pid);
if (fdset_size(fds) > 0)
log_warning("Got auxiliary fds with notification message, closing all.");
return 0;
}
static void invoke_sigchld_event(Manager *m, Unit *u, const siginfo_t *si) {
assert(m);
assert(u);
assert(si);
log_unit_debug(u, "Child "PID_FMT" belongs to %s", si->si_pid, u->id);
unit_unwatch_pid(u, si->si_pid);
UNIT_VTABLE(u)->sigchld_event(u, si->si_pid, si->si_code, si->si_status);
}
static int manager_dispatch_sigchld(Manager *m) {
assert(m);
for (;;) {
siginfo_t si = {};
/* First we call waitd() for a PID and do not reap the
* zombie. That way we can still access /proc/$PID for
* it while it is a zombie. */
if (waitid(P_ALL, 0, &si, WEXITED|WNOHANG|WNOWAIT) < 0) {
if (errno == ECHILD)
break;
if (errno == EINTR)
continue;
return -errno;
}
if (si.si_pid <= 0)
break;
if (si.si_code == CLD_EXITED || si.si_code == CLD_KILLED || si.si_code == CLD_DUMPED) {
_cleanup_free_ char *name = NULL;
Unit *u1, *u2, *u3;
get_process_comm(si.si_pid, &name);
log_debug("Child "PID_FMT" (%s) died (code=%s, status=%i/%s)",
si.si_pid, strna(name),
sigchld_code_to_string(si.si_code),
si.si_status,
strna(si.si_code == CLD_EXITED
? exit_status_to_string(si.si_status, EXIT_STATUS_FULL)
: signal_to_string(si.si_status)));
/* And now figure out the unit this belongs
* to, it might be multiple... */
u1 = manager_get_unit_by_pid_cgroup(m, si.si_pid);
if (u1)
invoke_sigchld_event(m, u1, &si);
u2 = hashmap_get(m->watch_pids1, PID_TO_PTR(si.si_pid));
if (u2 && u2 != u1)
invoke_sigchld_event(m, u2, &si);
u3 = hashmap_get(m->watch_pids2, PID_TO_PTR(si.si_pid));
if (u3 && u3 != u2 && u3 != u1)
invoke_sigchld_event(m, u3, &si);
}
/* And now, we actually reap the zombie. */
if (waitid(P_PID, si.si_pid, &si, WEXITED) < 0) {
if (errno == EINTR)
continue;
return -errno;
}
}
return 0;
}
static int manager_start_target(Manager *m, const char *name, JobMode mode) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
int r;
log_debug("Activating special unit %s", name);
r = manager_add_job_by_name(m, JOB_START, name, mode, &error, NULL);
if (r < 0)
log_error("Failed to enqueue %s job: %s", name, bus_error_message(&error, r));
return r;
}
static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
Manager *m = userdata;
ssize_t n;
struct signalfd_siginfo sfsi;
bool sigchld = false;
int r;
assert(m);
assert(m->signal_fd == fd);
if (revents != EPOLLIN) {
log_warning("Got unexpected events from signal file descriptor.");
return 0;
}
for (;;) {
n = read(m->signal_fd, &sfsi, sizeof(sfsi));
if (n != sizeof(sfsi)) {
if (n >= 0)
return -EIO;
if (errno == EINTR || errno == EAGAIN)
break;
return -errno;
}
log_received_signal(sfsi.ssi_signo == SIGCHLD ||
(sfsi.ssi_signo == SIGTERM && m->running_as == MANAGER_USER)
? LOG_DEBUG : LOG_INFO,
&sfsi);
switch (sfsi.ssi_signo) {
case SIGCHLD:
sigchld = true;
break;
case SIGTERM:
if (m->running_as == MANAGER_SYSTEM) {
/* This is for compatibility with the
* original sysvinit */
m->exit_code = MANAGER_REEXECUTE;
break;
}
/* Fall through */
case SIGINT:
if (m->running_as == MANAGER_SYSTEM) {
/* If the user presses C-A-D more than
* 7 times within 2s, we reboot
* immediately. */
if (ratelimit_test(&m->ctrl_alt_del_ratelimit))
manager_start_target(m, SPECIAL_CTRL_ALT_DEL_TARGET, JOB_REPLACE_IRREVERSIBLY);
else {
log_notice("Ctrl-Alt-Del was pressed more than 7 times within 2s, rebooting immediately.");
status_printf(NULL, true, false, "Ctrl-Alt-Del was pressed more than 7 times within 2s, rebooting immediately.");
m->exit_code = MANAGER_REBOOT;
}
break;
}
/* Run the exit target if there is one, if not, just exit. */
if (manager_start_target(m, SPECIAL_EXIT_TARGET, JOB_REPLACE) < 0) {
m->exit_code = MANAGER_EXIT;
return 0;
}
break;
case SIGWINCH:
if (m->running_as == MANAGER_SYSTEM)
manager_start_target(m, SPECIAL_KBREQUEST_TARGET, JOB_REPLACE);
/* This is a nop on non-init */
break;
case SIGPWR:
if (m->running_as == MANAGER_SYSTEM)
manager_start_target(m, SPECIAL_SIGPWR_TARGET, JOB_REPLACE);
/* This is a nop on non-init */
break;
case SIGUSR1: {
Unit *u;
u = manager_get_unit(m, SPECIAL_DBUS_SERVICE);
if (!u || UNIT_IS_ACTIVE_OR_RELOADING(unit_active_state(u))) {
log_info("Trying to reconnect to bus...");
bus_init(m, true);
}
if (!u || !UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(u))) {
log_info("Loading D-Bus service...");
manager_start_target(m, SPECIAL_DBUS_SERVICE, JOB_REPLACE);
}
break;
}
case SIGUSR2: {
_cleanup_free_ char *dump = NULL;
_cleanup_fclose_ FILE *f = NULL;
size_t size;
f = open_memstream(&dump, &size);
if (!f) {
log_warning_errno(errno, "Failed to allocate memory stream: %m");
break;
}
manager_dump_units(m, f, "\t");
manager_dump_jobs(m, f, "\t");
r = fflush_and_check(f);
if (r < 0) {
log_warning_errno(r, "Failed to write status stream: %m");
break;
}
log_dump(LOG_INFO, dump);
break;
}
case SIGHUP:
m->exit_code = MANAGER_RELOAD;
break;
default: {
/* Starting SIGRTMIN+0 */
static const char * const target_table[] = {
[0] = SPECIAL_DEFAULT_TARGET,
[1] = SPECIAL_RESCUE_TARGET,
[2] = SPECIAL_EMERGENCY_TARGET,
[3] = SPECIAL_HALT_TARGET,
[4] = SPECIAL_POWEROFF_TARGET,
[5] = SPECIAL_REBOOT_TARGET,
[6] = SPECIAL_KEXEC_TARGET
};
/* Starting SIGRTMIN+13, so that target halt and system halt are 10 apart */
static const ManagerExitCode code_table[] = {
[0] = MANAGER_HALT,
[1] = MANAGER_POWEROFF,
[2] = MANAGER_REBOOT,
[3] = MANAGER_KEXEC
};
if ((int) sfsi.ssi_signo >= SIGRTMIN+0 &&
(int) sfsi.ssi_signo < SIGRTMIN+(int) ELEMENTSOF(target_table)) {
int idx = (int) sfsi.ssi_signo - SIGRTMIN;
manager_start_target(m, target_table[idx],
(idx == 1 || idx == 2) ? JOB_ISOLATE : JOB_REPLACE);
break;
}
if ((int) sfsi.ssi_signo >= SIGRTMIN+13 &&
(int) sfsi.ssi_signo < SIGRTMIN+13+(int) ELEMENTSOF(code_table)) {
m->exit_code = code_table[sfsi.ssi_signo - SIGRTMIN - 13];
break;
}
switch (sfsi.ssi_signo - SIGRTMIN) {
case 20:
manager_set_show_status(m, SHOW_STATUS_YES);
break;
case 21:
manager_set_show_status(m, SHOW_STATUS_NO);
break;
case 22:
log_set_max_level(LOG_DEBUG);
log_info("Setting log level to debug.");
break;
case 23:
log_set_max_level(LOG_INFO);
log_info("Setting log level to info.");
break;
case 24:
if (m->running_as == MANAGER_USER) {
m->exit_code = MANAGER_EXIT;
return 0;
}
/* This is a nop on init */
break;
case 26:
case 29: /* compatibility: used to be mapped to LOG_TARGET_SYSLOG_OR_KMSG */
log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
log_notice("Setting log target to journal-or-kmsg.");
break;
case 27:
log_set_target(LOG_TARGET_CONSOLE);
log_notice("Setting log target to console.");
break;
case 28:
log_set_target(LOG_TARGET_KMSG);
log_notice("Setting log target to kmsg.");
break;
default:
log_warning("Got unhandled signal <%s>.", signal_to_string(sfsi.ssi_signo));
}
}
}
}
if (sigchld)
manager_dispatch_sigchld(m);
return 0;
}
static int manager_dispatch_time_change_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
Manager *m = userdata;
Iterator i;
Unit *u;
assert(m);
assert(m->time_change_fd == fd);
log_struct(LOG_INFO,
LOG_MESSAGE_ID(SD_MESSAGE_TIME_CHANGE),
LOG_MESSAGE("Time has been changed"),
NULL);
/* Restart the watch */
m->time_change_event_source = sd_event_source_unref(m->time_change_event_source);
m->time_change_fd = safe_close(m->time_change_fd);
manager_setup_time_change(m);
HASHMAP_FOREACH(u, m->units, i)
if (UNIT_VTABLE(u)->time_change)
UNIT_VTABLE(u)->time_change(u);
return 0;
}
static int manager_dispatch_idle_pipe_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
Manager *m = userdata;
assert(m);
assert(m->idle_pipe[2] == fd);
m->no_console_output = m->n_on_console > 0;
manager_close_idle_pipe(m);
return 0;
}
static int manager_dispatch_jobs_in_progress(sd_event_source *source, usec_t usec, void *userdata) {
Manager *m = userdata;
int r;
uint64_t next;
assert(m);
assert(source);
manager_print_jobs_in_progress(m);
next = now(CLOCK_MONOTONIC) + JOBS_IN_PROGRESS_PERIOD_USEC;
r = sd_event_source_set_time(source, next);
if (r < 0)
return r;
return sd_event_source_set_enabled(source, SD_EVENT_ONESHOT);
}
int manager_loop(Manager *m) {
int r;
RATELIMIT_DEFINE(rl, 1*USEC_PER_SEC, 50000);
assert(m);
m->exit_code = MANAGER_OK;
/* Release the path cache */
m->unit_path_cache = set_free_free(m->unit_path_cache);
manager_check_finished(m);
/* There might still be some zombies hanging around from
* before we were exec()'ed. Let's reap them. */
r = manager_dispatch_sigchld(m);
if (r < 0)
return r;
while (m->exit_code == MANAGER_OK) {
usec_t wait_usec;
if (m->runtime_watchdog > 0 && m->running_as == MANAGER_SYSTEM)
watchdog_ping();
if (!ratelimit_test(&rl)) {
/* Yay, something is going seriously wrong, pause a little */
log_warning("Looping too fast. Throttling execution a little.");
sleep(1);
}
if (manager_dispatch_load_queue(m) > 0)
continue;
if (manager_dispatch_gc_queue(m) > 0)
continue;
if (manager_dispatch_cleanup_queue(m) > 0)
continue;
if (manager_dispatch_cgroup_queue(m) > 0)
continue;
if (manager_dispatch_dbus_queue(m) > 0)
continue;
/* Sleep for half the watchdog time */
if (m->runtime_watchdog > 0 && m->running_as == MANAGER_SYSTEM) {
wait_usec = m->runtime_watchdog / 2;
if (wait_usec <= 0)
wait_usec = 1;
} else
wait_usec = USEC_INFINITY;
r = sd_event_run(m->event, wait_usec);
if (r < 0)
return log_error_errno(r, "Failed to run event loop: %m");
}
return m->exit_code;
}
int manager_load_unit_from_dbus_path(Manager *m, const char *s, sd_bus_error *e, Unit **_u) {
_cleanup_free_ char *n = NULL;
Unit *u;
int r;
assert(m);
assert(s);
assert(_u);
r = unit_name_from_dbus_path(s, &n);
if (r < 0)
return r;
r = manager_load_unit(m, n, NULL, e, &u);
if (r < 0)
return r;
*_u = u;
return 0;
}
int manager_get_job_from_dbus_path(Manager *m, const char *s, Job **_j) {
const char *p;
unsigned id;
Job *j;
int r;
assert(m);
assert(s);
assert(_j);
p = startswith(s, "/org/freedesktop/systemd1/job/");
if (!p)
return -EINVAL;
r = safe_atou(p, &id);
if (r < 0)
return r;
j = manager_get_job(m, id);
if (!j)
return -ENOENT;
*_j = j;
return 0;
}
void manager_send_unit_audit(Manager *m, Unit *u, int type, bool success) {
#ifdef HAVE_AUDIT
_cleanup_free_ char *p = NULL;
const char *msg;
int audit_fd, r;
if (m->running_as != MANAGER_SYSTEM)
return;
audit_fd = get_audit_fd();
if (audit_fd < 0)
return;
/* Don't generate audit events if the service was already
* started and we're just deserializing */
if (m->n_reloading > 0)
return;
if (u->type != UNIT_SERVICE)
return;
r = unit_name_to_prefix_and_instance(u->id, &p);
if (r < 0) {
log_error_errno(r, "Failed to extract prefix and instance of unit name: %m");
return;
}
msg = strjoina("unit=", p);
if (audit_log_user_comm_message(audit_fd, type, msg, "systemd", NULL, NULL, NULL, success) < 0) {
if (errno == EPERM)
/* We aren't allowed to send audit messages?
* Then let's not retry again. */
close_audit_fd();
else
log_warning_errno(errno, "Failed to send audit message: %m");
}
#endif
}
void manager_send_unit_plymouth(Manager *m, Unit *u) {
union sockaddr_union sa = PLYMOUTH_SOCKET;
int n = 0;
_cleanup_free_ char *message = NULL;
_cleanup_close_ int fd = -1;
/* Don't generate plymouth events if the service was already
* started and we're just deserializing */
if (m->n_reloading > 0)
return;
if (m->running_as != MANAGER_SYSTEM)
return;
if (detect_container() > 0)
return;
if (u->type != UNIT_SERVICE &&
u->type != UNIT_MOUNT &&
u->type != UNIT_SWAP)
return;
/* We set SOCK_NONBLOCK here so that we rather drop the
* message then wait for plymouth */
fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
if (fd < 0) {
log_error_errno(errno, "socket() failed: %m");
return;
}
if (connect(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + 1 + strlen(sa.un.sun_path+1)) < 0) {
if (!IN_SET(errno, EPIPE, EAGAIN, ENOENT, ECONNREFUSED, ECONNRESET, ECONNABORTED))
log_error_errno(errno, "connect() failed: %m");
return;
}
if (asprintf(&message, "U\002%c%s%n", (int) (strlen(u->id) + 1), u->id, &n) < 0) {
log_oom();
return;
}
errno = 0;
if (write(fd, message, n + 1) != n + 1)
if (!IN_SET(errno, EPIPE, EAGAIN, ENOENT, ECONNREFUSED, ECONNRESET, ECONNABORTED))
log_error_errno(errno, "Failed to write Plymouth message: %m");
}
int manager_open_serialization(Manager *m, FILE **_f) {
const char *path;
int fd = -1;
FILE *f;
assert(_f);
path = m->running_as == MANAGER_SYSTEM ? "/run/systemd" : "/tmp";
fd = open_tmpfile(path, O_RDWR|O_CLOEXEC);
if (fd < 0)
return -errno;
log_debug("Serializing state to %s", path);
f = fdopen(fd, "w+");
if (!f) {
safe_close(fd);
return -errno;
}
*_f = f;
return 0;
}
int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool switching_root) {
Iterator i;
Unit *u;
const char *t;
char **e;
int r;
assert(m);
assert(f);
assert(fds);
m->n_reloading ++;
fprintf(f, "current-job-id=%"PRIu32"\n", m->current_job_id);
fprintf(f, "taint-usr=%s\n", yes_no(m->taint_usr));
fprintf(f, "n-installed-jobs=%u\n", m->n_installed_jobs);
fprintf(f, "n-failed-jobs=%u\n", m->n_failed_jobs);
dual_timestamp_serialize(f, "firmware-timestamp", &m->firmware_timestamp);
dual_timestamp_serialize(f, "loader-timestamp", &m->loader_timestamp);
dual_timestamp_serialize(f, "kernel-timestamp", &m->kernel_timestamp);
dual_timestamp_serialize(f, "initrd-timestamp", &m->initrd_timestamp);
if (!in_initrd()) {
dual_timestamp_serialize(f, "userspace-timestamp", &m->userspace_timestamp);
dual_timestamp_serialize(f, "finish-timestamp", &m->finish_timestamp);
dual_timestamp_serialize(f, "security-start-timestamp", &m->security_start_timestamp);
dual_timestamp_serialize(f, "security-finish-timestamp", &m->security_finish_timestamp);
dual_timestamp_serialize(f, "generators-start-timestamp", &m->generators_start_timestamp);
dual_timestamp_serialize(f, "generators-finish-timestamp", &m->generators_finish_timestamp);
dual_timestamp_serialize(f, "units-load-start-timestamp", &m->units_load_start_timestamp);
dual_timestamp_serialize(f, "units-load-finish-timestamp", &m->units_load_finish_timestamp);
}
if (!switching_root) {
STRV_FOREACH(e, m->environment) {
_cleanup_free_ char *ce;
ce = cescape(*e);
if (!ce)
return -ENOMEM;
fprintf(f, "env=%s\n", *e);
}
}
if (m->notify_fd >= 0) {
int copy;
copy = fdset_put_dup(fds, m->notify_fd);
if (copy < 0)
return copy;
fprintf(f, "notify-fd=%i\n", copy);
fprintf(f, "notify-socket=%s\n", m->notify_socket);
}
if (m->kdbus_fd >= 0) {
int copy;
copy = fdset_put_dup(fds, m->kdbus_fd);
if (copy < 0)
return copy;
fprintf(f, "kdbus-fd=%i\n", copy);
}
bus_track_serialize(m->subscribed, f);
fputc('\n', f);
HASHMAP_FOREACH_KEY(u, t, m->units, i) {
if (u->id != t)
continue;
/* Start marker */
fputs(u->id, f);
fputc('\n', f);
r = unit_serialize(u, f, fds, !switching_root);
if (r < 0) {
m->n_reloading --;
return r;
}
}
assert(m->n_reloading > 0);
m->n_reloading --;
if (ferror(f))
return -EIO;
r = bus_fdset_add_all(m, fds);
if (r < 0)
return r;
return 0;
}
int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
int r = 0;
assert(m);
assert(f);
log_debug("Deserializing state...");
m->n_reloading ++;
for (;;) {
char line[LINE_MAX], *l;
if (!fgets(line, sizeof(line), f)) {
if (feof(f))
r = 0;
else
r = -errno;
goto finish;
}
char_array_0(line);
l = strstrip(line);
if (l[0] == 0)
break;
if (startswith(l, "current-job-id=")) {
uint32_t id;
if (safe_atou32(l+15, &id) < 0)
log_debug("Failed to parse current job id value %s", l+15);
else
m->current_job_id = MAX(m->current_job_id, id);
} else if (startswith(l, "n-installed-jobs=")) {
uint32_t n;
if (safe_atou32(l+17, &n) < 0)
log_debug("Failed to parse installed jobs counter %s", l+17);
else
m->n_installed_jobs += n;
} else if (startswith(l, "n-failed-jobs=")) {
uint32_t n;
if (safe_atou32(l+14, &n) < 0)
log_debug("Failed to parse failed jobs counter %s", l+14);
else
m->n_failed_jobs += n;
} else if (startswith(l, "taint-usr=")) {
int b;
b = parse_boolean(l+10);
if (b < 0)
log_debug("Failed to parse taint /usr flag %s", l+10);
else
m->taint_usr = m->taint_usr || b;
} else if (startswith(l, "firmware-timestamp="))
dual_timestamp_deserialize(l+19, &m->firmware_timestamp);
else if (startswith(l, "loader-timestamp="))
dual_timestamp_deserialize(l+17, &m->loader_timestamp);
else if (startswith(l, "kernel-timestamp="))
dual_timestamp_deserialize(l+17, &m->kernel_timestamp);
else if (startswith(l, "initrd-timestamp="))
dual_timestamp_deserialize(l+17, &m->initrd_timestamp);
else if (startswith(l, "userspace-timestamp="))
dual_timestamp_deserialize(l+20, &m->userspace_timestamp);
else if (startswith(l, "finish-timestamp="))
dual_timestamp_deserialize(l+17, &m->finish_timestamp);
else if (startswith(l, "security-start-timestamp="))
dual_timestamp_deserialize(l+25, &m->security_start_timestamp);
else if (startswith(l, "security-finish-timestamp="))
dual_timestamp_deserialize(l+26, &m->security_finish_timestamp);
else if (startswith(l, "generators-start-timestamp="))
dual_timestamp_deserialize(l+27, &m->generators_start_timestamp);
else if (startswith(l, "generators-finish-timestamp="))
dual_timestamp_deserialize(l+28, &m->generators_finish_timestamp);
else if (startswith(l, "units-load-start-timestamp="))
dual_timestamp_deserialize(l+27, &m->units_load_start_timestamp);
else if (startswith(l, "units-load-finish-timestamp="))
dual_timestamp_deserialize(l+28, &m->units_load_finish_timestamp);
else if (startswith(l, "env=")) {
_cleanup_free_ char *uce = NULL;
char **e;
r = cunescape(l + 4, UNESCAPE_RELAX, &uce);
if (r < 0)
goto finish;
e = strv_env_set(m->environment, uce);
if (!e) {
r = -ENOMEM;
goto finish;
}
strv_free(m->environment);
m->environment = e;
} else if (startswith(l, "notify-fd=")) {
int fd;
if (safe_atoi(l + 10, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd))
log_debug("Failed to parse notify fd: %s", l + 10);
else {
m->notify_event_source = sd_event_source_unref(m->notify_event_source);
safe_close(m->notify_fd);
m->notify_fd = fdset_remove(fds, fd);
}
} else if (startswith(l, "notify-socket=")) {
char *n;
n = strdup(l+14);
if (!n) {
r = -ENOMEM;
goto finish;
}
free(m->notify_socket);
m->notify_socket = n;
} else if (startswith(l, "kdbus-fd=")) {
int fd;
if (safe_atoi(l + 9, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd))
log_debug("Failed to parse kdbus fd: %s", l + 9);
else {
safe_close(m->kdbus_fd);
m->kdbus_fd = fdset_remove(fds, fd);
}
} else {
int k;
k = bus_track_deserialize_item(&m->deserialized_subscribed, l);
if (k < 0)
log_debug_errno(k, "Failed to deserialize bus tracker object: %m");
else if (k == 0)
log_debug("Unknown serialization item '%s'", l);
}
}
for (;;) {
Unit *u;
char name[UNIT_NAME_MAX+2];
/* Start marker */
if (!fgets(name, sizeof(name), f)) {
if (feof(f))
r = 0;
else
r = -errno;
goto finish;
}
char_array_0(name);
r = manager_load_unit(m, strstrip(name), NULL, NULL, &u);
if (r < 0)
goto finish;
r = unit_deserialize(u, f, fds);
if (r < 0)
goto finish;
}
finish:
if (ferror(f))
r = -EIO;
assert(m->n_reloading > 0);
m->n_reloading --;
return r;
}
int manager_reload(Manager *m) {
int r, q;
_cleanup_fclose_ FILE *f = NULL;
_cleanup_fdset_free_ FDSet *fds = NULL;
assert(m);
r = manager_open_serialization(m, &f);
if (r < 0)
return r;
m->n_reloading ++;
bus_manager_send_reloading(m, true);
fds = fdset_new();
if (!fds) {
m->n_reloading --;
return -ENOMEM;
}
r = manager_serialize(m, f, fds, false);
if (r < 0) {
m->n_reloading --;
return r;
}
if (fseeko(f, 0, SEEK_SET) < 0) {
m->n_reloading --;
return -errno;
}
/* From here on there is no way back. */
manager_clear_jobs_and_units(m);
manager_undo_generators(m);
lookup_paths_free(&m->lookup_paths);
/* Find new unit paths */
q = manager_run_generators(m);
if (q < 0 && r >= 0)
r = q;
q = lookup_paths_init(
&m->lookup_paths, m->running_as, true,
NULL,
m->generator_unit_path,
m->generator_unit_path_early,
m->generator_unit_path_late);
if (q < 0 && r >= 0)
r = q;
manager_build_unit_path_cache(m);
/* First, enumerate what we can from all config files */
manager_enumerate(m);
/* Second, deserialize our stored data */
q = manager_deserialize(m, f, fds);
if (q < 0 && r >= 0)
r = q;
fclose(f);
f = NULL;
/* Re-register notify_fd as event source */
q = manager_setup_notify(m);
if (q < 0 && r >= 0)
r = q;
/* Third, fire things up! */
manager_coldplug(m);
/* Sync current state of bus names with our set of listening units */
if (m->api_bus)
manager_sync_bus_names(m, m->api_bus);
assert(m->n_reloading > 0);
m->n_reloading--;
m->send_reloading_done = true;
return r;
}
bool manager_is_reloading_or_reexecuting(Manager *m) {
assert(m);
return m->n_reloading != 0;
}
void manager_reset_failed(Manager *m) {
Unit *u;
Iterator i;
assert(m);
HASHMAP_FOREACH(u, m->units, i)
unit_reset_failed(u);
}
bool manager_unit_inactive_or_pending(Manager *m, const char *name) {
Unit *u;
assert(m);
assert(name);
/* Returns true if the unit is inactive or going down */
u = manager_get_unit(m, name);
if (!u)
return true;
return unit_inactive_or_pending(u);
}
static void manager_notify_finished(Manager *m) {
char userspace[FORMAT_TIMESPAN_MAX], initrd[FORMAT_TIMESPAN_MAX], kernel[FORMAT_TIMESPAN_MAX], sum[FORMAT_TIMESPAN_MAX];
usec_t firmware_usec, loader_usec, kernel_usec, initrd_usec, userspace_usec, total_usec;
if (m->test_run)
return;
if (m->running_as == MANAGER_SYSTEM && detect_container() <= 0) {
/* Note that m->kernel_usec.monotonic is always at 0,
* and m->firmware_usec.monotonic and
* m->loader_usec.monotonic should be considered
* negative values. */
firmware_usec = m->firmware_timestamp.monotonic - m->loader_timestamp.monotonic;
loader_usec = m->loader_timestamp.monotonic - m->kernel_timestamp.monotonic;
userspace_usec = m->finish_timestamp.monotonic - m->userspace_timestamp.monotonic;
total_usec = m->firmware_timestamp.monotonic + m->finish_timestamp.monotonic;
if (dual_timestamp_is_set(&m->initrd_timestamp)) {
kernel_usec = m->initrd_timestamp.monotonic - m->kernel_timestamp.monotonic;
initrd_usec = m->userspace_timestamp.monotonic - m->initrd_timestamp.monotonic;
log_struct(LOG_INFO,
LOG_MESSAGE_ID(SD_MESSAGE_STARTUP_FINISHED),
"KERNEL_USEC="USEC_FMT, kernel_usec,
"INITRD_USEC="USEC_FMT, initrd_usec,
"USERSPACE_USEC="USEC_FMT, userspace_usec,
LOG_MESSAGE("Startup finished in %s (kernel) + %s (initrd) + %s (userspace) = %s.",
format_timespan(kernel, sizeof(kernel), kernel_usec, USEC_PER_MSEC),
format_timespan(initrd, sizeof(initrd), initrd_usec, USEC_PER_MSEC),
format_timespan(userspace, sizeof(userspace), userspace_usec, USEC_PER_MSEC),
format_timespan(sum, sizeof(sum), total_usec, USEC_PER_MSEC)),
NULL);
} else {
kernel_usec = m->userspace_timestamp.monotonic - m->kernel_timestamp.monotonic;
initrd_usec = 0;
log_struct(LOG_INFO,
LOG_MESSAGE_ID(SD_MESSAGE_STARTUP_FINISHED),
"KERNEL_USEC="USEC_FMT, kernel_usec,
"USERSPACE_USEC="USEC_FMT, userspace_usec,
LOG_MESSAGE("Startup finished in %s (kernel) + %s (userspace) = %s.",
format_timespan(kernel, sizeof(kernel), kernel_usec, USEC_PER_MSEC),
format_timespan(userspace, sizeof(userspace), userspace_usec, USEC_PER_MSEC),
format_timespan(sum, sizeof(sum), total_usec, USEC_PER_MSEC)),
NULL);
}
} else {
firmware_usec = loader_usec = initrd_usec = kernel_usec = 0;
total_usec = userspace_usec = m->finish_timestamp.monotonic - m->userspace_timestamp.monotonic;
log_struct(LOG_INFO,
LOG_MESSAGE_ID(SD_MESSAGE_STARTUP_FINISHED),
"USERSPACE_USEC="USEC_FMT, userspace_usec,
LOG_MESSAGE("Startup finished in %s.",
format_timespan(sum, sizeof(sum), total_usec, USEC_PER_MSEC)),
NULL);
}
bus_manager_send_finished(m, firmware_usec, loader_usec, kernel_usec, initrd_usec, userspace_usec, total_usec);
sd_notifyf(false,
"READY=1\n"
"STATUS=Startup finished in %s.",
format_timespan(sum, sizeof(sum), total_usec, USEC_PER_MSEC));
}
void manager_check_finished(Manager *m) {
assert(m);
if (m->n_reloading > 0)
return;
/* Verify that we are actually running currently. Initially
* the exit code is set to invalid, and during operation it is
* then set to MANAGER_OK */
if (m->exit_code != MANAGER_OK)
return;
if (hashmap_size(m->jobs) > 0) {
if (m->jobs_in_progress_event_source)
/* Ignore any failure, this is only for feedback */
(void) sd_event_source_set_time(m->jobs_in_progress_event_source, now(CLOCK_MONOTONIC) + JOBS_IN_PROGRESS_WAIT_USEC);
return;
}
manager_flip_auto_status(m, false);
/* Notify Type=idle units that we are done now */
manager_close_idle_pipe(m);
/* Turn off confirm spawn now */
m->confirm_spawn = false;
/* No need to update ask password status when we're going non-interactive */
manager_close_ask_password(m);
/* This is no longer the first boot */
manager_set_first_boot(m, false);
if (dual_timestamp_is_set(&m->finish_timestamp))
return;
dual_timestamp_get(&m->finish_timestamp);
manager_notify_finished(m);
manager_invalidate_startup_units(m);
}
static int create_generator_dir(Manager *m, char **generator, const char *name) {
char *p;
int r;
assert(m);
assert(generator);
assert(name);
if (*generator)
return 0;
if (m->running_as == MANAGER_SYSTEM && getpid() == 1) {
/* systemd --system, not running --test */
p = strappend("/run/systemd/", name);
if (!p)
return log_oom();
r = mkdir_p_label(p, 0755);
if (r < 0) {
log_error_errno(r, "Failed to create generator directory %s: %m", p);
free(p);
return r;
}
} else if (m->running_as == MANAGER_USER) {
const char *s = NULL;
s = getenv("XDG_RUNTIME_DIR");
if (!s)
return -EINVAL;
p = strjoin(s, "/systemd/", name, NULL);
if (!p)
return log_oom();
r = mkdir_p_label(p, 0755);
if (r < 0) {
log_error_errno(r, "Failed to create generator directory %s: %m", p);
free(p);
return r;
}
} else {
/* systemd --system --test */
p = strjoin("/tmp/systemd-", name, ".XXXXXX", NULL);
if (!p)
return log_oom();
if (!mkdtemp(p)) {
log_error_errno(errno, "Failed to create generator directory %s: %m", p);
free(p);
return -errno;
}
}
*generator = p;
return 0;
}
static void trim_generator_dir(Manager *m, char **generator) {
assert(m);
assert(generator);
if (!*generator)
return;
if (rmdir(*generator) >= 0)
*generator = mfree(*generator);
return;
}
static int manager_run_generators(Manager *m) {
_cleanup_strv_free_ char **paths = NULL;
const char *argv[5];
char **path;
int r;
assert(m);
if (m->test_run)
return 0;
paths = generator_paths(m->running_as);
if (!paths)
return log_oom();
/* Optimize by skipping the whole process by not creating output directories
* if no generators are found. */
STRV_FOREACH(path, paths) {
r = access(*path, F_OK);
if (r == 0)
goto found;
if (errno != ENOENT)
log_warning_errno(errno, "Failed to open generator directory %s: %m", *path);
}
return 0;
found:
r = create_generator_dir(m, &m->generator_unit_path, "generator");
if (r < 0)
goto finish;
r = create_generator_dir(m, &m->generator_unit_path_early, "generator.early");
if (r < 0)
goto finish;
r = create_generator_dir(m, &m->generator_unit_path_late, "generator.late");
if (r < 0)
goto finish;
argv[0] = NULL; /* Leave this empty, execute_directory() will fill something in */
argv[1] = m->generator_unit_path;
argv[2] = m->generator_unit_path_early;
argv[3] = m->generator_unit_path_late;
argv[4] = NULL;
RUN_WITH_UMASK(0022)
execute_directories((const char* const*) paths, DEFAULT_TIMEOUT_USEC, (char**) argv);
finish:
trim_generator_dir(m, &m->generator_unit_path);
trim_generator_dir(m, &m->generator_unit_path_early);
trim_generator_dir(m, &m->generator_unit_path_late);
return r;
}
static void remove_generator_dir(Manager *m, char **generator) {
assert(m);
assert(generator);
if (!*generator)
return;
strv_remove(m->lookup_paths.unit_path, *generator);
(void) rm_rf(*generator, REMOVE_ROOT);
*generator = mfree(*generator);
}
static void manager_undo_generators(Manager *m) {
assert(m);
remove_generator_dir(m, &m->generator_unit_path);
remove_generator_dir(m, &m->generator_unit_path_early);
remove_generator_dir(m, &m->generator_unit_path_late);
}
int manager_environment_add(Manager *m, char **minus, char **plus) {
char **a = NULL, **b = NULL, **l;
assert(m);
l = m->environment;
if (!strv_isempty(minus)) {
a = strv_env_delete(l, 1, minus);
if (!a)
return -ENOMEM;
l = a;
}
if (!strv_isempty(plus)) {
b = strv_env_merge(2, l, plus);
if (!b) {
strv_free(a);
return -ENOMEM;
}
l = b;
}
if (m->environment != l)
strv_free(m->environment);
if (a != l)
strv_free(a);
if (b != l)
strv_free(b);
m->environment = l;
manager_clean_environment(m);
strv_sort(m->environment);
return 0;
}
int manager_set_default_rlimits(Manager *m, struct rlimit **default_rlimit) {
int i;
assert(m);
for (i = 0; i < _RLIMIT_MAX; i++) {
m->rlimit[i] = mfree(m->rlimit[i]);
if (!default_rlimit[i])
continue;
m->rlimit[i] = newdup(struct rlimit, default_rlimit[i], 1);
if (!m->rlimit[i])
return -ENOMEM;
}
return 0;
}
void manager_recheck_journal(Manager *m) {
Unit *u;
assert(m);
if (m->running_as != MANAGER_SYSTEM)
return;
u = manager_get_unit(m, SPECIAL_JOURNALD_SOCKET);
if (u && SOCKET(u)->state != SOCKET_RUNNING) {
log_close_journal();
return;
}
u = manager_get_unit(m, SPECIAL_JOURNALD_SERVICE);
if (u && SERVICE(u)->state != SERVICE_RUNNING) {
log_close_journal();
return;
}
/* Hmm, OK, so the socket is fully up and the service is up
* too, then let's make use of the thing. */
log_open();
}
void manager_set_show_status(Manager *m, ShowStatus mode) {
assert(m);
assert(IN_SET(mode, SHOW_STATUS_AUTO, SHOW_STATUS_NO, SHOW_STATUS_YES, SHOW_STATUS_TEMPORARY));
if (m->running_as != MANAGER_SYSTEM)
return;
if (m->show_status != mode)
log_debug("%s showing of status.",
mode == SHOW_STATUS_NO ? "Disabling" : "Enabling");
m->show_status = mode;
if (mode > 0)
(void) touch("/run/systemd/show-status");
else
(void) unlink("/run/systemd/show-status");
}
static bool manager_get_show_status(Manager *m, StatusType type) {
assert(m);
if (m->running_as != MANAGER_SYSTEM)
return false;
if (m->no_console_output)
return false;
if (!IN_SET(manager_state(m), MANAGER_INITIALIZING, MANAGER_STARTING, MANAGER_STOPPING))
return false;
/* If we cannot find out the status properly, just proceed. */
if (type != STATUS_TYPE_EMERGENCY && manager_check_ask_password(m) > 0)
return false;
if (m->show_status > 0)
return true;
return false;
}
void manager_set_first_boot(Manager *m, bool b) {
assert(m);
if (m->running_as != MANAGER_SYSTEM)
return;
if (m->first_boot != (int) b) {
if (b)
(void) touch("/run/systemd/first-boot");
else
(void) unlink("/run/systemd/first-boot");
}
m->first_boot = b;
}
void manager_status_printf(Manager *m, StatusType type, const char *status, const char *format, ...) {
va_list ap;
/* If m is NULL, assume we're after shutdown and let the messages through. */
if (m && !manager_get_show_status(m, type))
return;
/* XXX We should totally drop the check for ephemeral here
* and thus effectively make 'Type=idle' pointless. */
if (type == STATUS_TYPE_EPHEMERAL && m && m->n_on_console > 0)
return;
va_start(ap, format);
status_vprintf(status, true, type == STATUS_TYPE_EPHEMERAL, format, ap);
va_end(ap);
}
Set *manager_get_units_requiring_mounts_for(Manager *m, const char *path) {
char p[strlen(path)+1];
assert(m);
assert(path);
strcpy(p, path);
path_kill_slashes(p);
return hashmap_get(m->units_requiring_mounts_for, streq(p, "/") ? "" : p);
}
const char *manager_get_runtime_prefix(Manager *m) {
assert(m);
return m->running_as == MANAGER_SYSTEM ?
"/run" :
getenv("XDG_RUNTIME_DIR");
}
int manager_update_failed_units(Manager *m, Unit *u, bool failed) {
unsigned size;
int r;
assert(m);
assert(u->manager == m);
size = set_size(m->failed_units);
if (failed) {
r = set_ensure_allocated(&m->failed_units, NULL);
if (r < 0)
return log_oom();
if (set_put(m->failed_units, u) < 0)
return log_oom();
} else
(void) set_remove(m->failed_units, u);
if (set_size(m->failed_units) != size)
bus_manager_send_change_signal(m);
return 0;
}
ManagerState manager_state(Manager *m) {
Unit *u;
assert(m);
/* Did we ever finish booting? If not then we are still starting up */
if (!dual_timestamp_is_set(&m->finish_timestamp)) {
u = manager_get_unit(m, SPECIAL_BASIC_TARGET);
if (!u || !UNIT_IS_ACTIVE_OR_RELOADING(unit_active_state(u)))
return MANAGER_INITIALIZING;
return MANAGER_STARTING;
}
/* Is the special shutdown target queued? If so, we are in shutdown state */
u = manager_get_unit(m, SPECIAL_SHUTDOWN_TARGET);
if (u && u->job && IN_SET(u->job->type, JOB_START, JOB_RESTART, JOB_RELOAD_OR_START))
return MANAGER_STOPPING;
/* Are the rescue or emergency targets active or queued? If so we are in maintenance state */
u = manager_get_unit(m, SPECIAL_RESCUE_TARGET);
if (u && (UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(u)) ||
(u->job && IN_SET(u->job->type, JOB_START, JOB_RESTART, JOB_RELOAD_OR_START))))
return MANAGER_MAINTENANCE;
u = manager_get_unit(m, SPECIAL_EMERGENCY_TARGET);
if (u && (UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(u)) ||
(u->job && IN_SET(u->job->type, JOB_START, JOB_RESTART, JOB_RELOAD_OR_START))))
return MANAGER_MAINTENANCE;
/* Are there any failed units? If so, we are in degraded mode */
if (set_size(m->failed_units) > 0)
return MANAGER_DEGRADED;
return MANAGER_RUNNING;
}
static const char *const manager_state_table[_MANAGER_STATE_MAX] = {
[MANAGER_INITIALIZING] = "initializing",
[MANAGER_STARTING] = "starting",
[MANAGER_RUNNING] = "running",
[MANAGER_DEGRADED] = "degraded",
[MANAGER_MAINTENANCE] = "maintenance",
[MANAGER_STOPPING] = "stopping",
};
DEFINE_STRING_TABLE_LOOKUP(manager_state, ManagerState);