| SYSTEMD-SOFT-REBOOT.SERVICE(8) | systemd-soft-reboot.service | SYSTEMD-SOFT-REBOOT.SERVICE(8) |
نام (NAME)
systemd-soft-reboot.service - سرویس راهاندازی مجدد نرم لایه کاربری در systemd
خلاصه دستور (SYNOPSIS)
systemd-soft-reboot.service
توضیحات (DESCRIPTION)
systemd-soft-reboot.service یک سرویس سیستمی است که توسط soft-reboot.target فراخوانی میشود و مسئول انجام یک عملیات راهاندازی مجدد صرفاً در فضای کاربری (userspace-only) است. هنگام فراخوانی، این سرویس سیگنال SIGTERM را به هر فرایند در حال اجرای باقیمانده ارسال میکند (اما منتظر خروج فرایندها نمیماند) و سپس آن را با SIGKILL دنبال میکند. اگر دایرکتوری /run/nextroot/ وجود داشته باشد (که میتواند یک دایرکتوری معمولی، یک نقطه مانت دایرکتوری یا یک پیوند نمادین به هر یک از آنها باشد)، ریشه سیستم فایل را به آن تغییر میدهد. سپس مدیر سرویس را از روی سیستم فایل ریشه (که احتمالاً اکنون جدید است) مجدداً اجرا میکند، که همانند یک راهاندازی مجدد عادی یک تراکنش بوت جدید را در صف قرار میدهد.
چنین عملیات راهاندازی مجدد صرفاً در فضای کاربری امکان بهروزرسانی یا بازنشانی کل فضای کاربری را با کمترین زمان خرابی (downtime) فراهم میکند، زیرا عملیات راهاندازی مجدد از مراحل زیر گذر نمیکند:
با این حال، این نوع راهاندازی مجدد معایبی نیز به همراه دارد:
این محدودیتها ممکن است از روشهای گوناگونی برطرف شوند که خارج از حوصلهٔ این مستندات است، مانند وصله زدن زندهٔ هسته (kernel live-patching) و فایلهای /etc/sysctl.d/ بهاندازهٔ کافی جامع.
انتقال منابع (RESOURCE PASS-THROUGH)
منابع گوناگون زمان اجرای سیستمعامل میتوانند از یک زمان اجرای سیستم به زمان اجرای بعدی از طریق عملیات راهاندازی مجدد در فضای کاربری انتقال یابند. بهطور مشخص:
[Unit] Description=My Surviving Service SurviveFinalKillSignal=yes IgnoreOnIsolate=yes DefaultDependencies=no After=basic.target Conflicts=reboot.target kexec.target poweroff.target halt.target rescue.target emergency.target Before=shutdown.target rescue.target emergency.target [Service] Type=oneshot ExecStart=sleep infinity
[Unit] SurviveFinalKillSignal=yes IgnoreOnIsolate=yes DefaultDependencies=no
/* SPDX-License-Identifier: MIT-0 */
/* A D-Bus service that automatically reconnects when the system bus is
* restarted.
*
* Compile with 'cc sd_bus_service_reconnect.c $(pkg-config --libs --cflags libsystemd)'
*
* To allow the program to take ownership of the name 'org.freedesktop.ReconnectExample',
* add the following as /etc/dbus-1/system.d/org.freedesktop.ReconnectExample.conf
* and then reload the broker with 'systemctl reload dbus':
<?xml version="1.0"?> <!--*-nxml-*-->
<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
<busconfig>
<policy user="root">
<allow own="org.freedesktop.ReconnectExample"/>
<allow send_destination="org.freedesktop.ReconnectExample"/>
<allow receive_sender="org.freedesktop.ReconnectExample"/>
</policy>
<policy context="default">
<allow send_destination="org.freedesktop.ReconnectExample"/>
<allow receive_sender="org.freedesktop.ReconnectExample"/>
</policy>
</busconfig>
*
* To get the property via busctl:
*
* $ busctl --user get-property org.freedesktop.ReconnectExample \
* /org/freedesktop/ReconnectExample \
* org.freedesktop.ReconnectExample \
* Example
* s "example"
*/
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <systemd/sd-bus.h>
#define _cleanup_(f) __attribute__((cleanup(f)))
static int log_error(int r, const char *str) {
fprintf(stderr, "%s failed: %s\n", str, strerror(-r));
return r;
}
typedef struct object {
const char *example;
sd_bus **bus;
sd_event **event;
} object;
static int property_get(
sd_bus *bus,
const char *path,
const char *interface,
const char *property,
sd_bus_message *reply,
void *userdata,
sd_bus_error *error) {
object *o = userdata;
if (strcmp(property, "Example") == 0)
return sd_bus_message_append(reply, "s", o->example);
return sd_bus_error_setf(error,
SD_BUS_ERROR_UNKNOWN_PROPERTY,
"Unknown property '%s'",
property);
}
/* https://www.freedesktop.org/software/systemd/man/sd_bus_add_object.html */
static const sd_bus_vtable vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_PROPERTY(
"Example", "s",
property_get,
0,
SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_VTABLE_END
};
static int setup(object *o);
static int on_disconnect(sd_bus_message *message, void *userdata, sd_bus_error *ret_error) {
int r;
r = setup((object *)userdata);
if (r < 0) {
object *o = userdata;
r = sd_event_exit(*o->event, r);
if (r < 0)
return log_error(r, "sd_event_exit()");
}
return 1;
}
/* Ensure the event loop exits with a clear error if acquiring the well-known
* service name fails */
static int request_name_callback(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
int r;
if (!sd_bus_message_is_method_error(m, NULL))
return 1;
const sd_bus_error *error = sd_bus_message_get_error(m);
if (sd_bus_error_has_names(error, SD_BUS_ERROR_TIMEOUT, SD_BUS_ERROR_NO_REPLY))
return 1; /* The bus is not available, try again later */
fprintf(stderr, "Failed to request name: %s\n", error->message);
object *o = userdata;
r = sd_event_exit(*o->event, -sd_bus_error_get_errno(error));
if (r < 0)
return log_error(r, "sd_event_exit()");
return 1;
}
static int setup(object *o) {
int r;
/* If we are reconnecting, then the bus object needs to be closed, detached
* from the event loop and recreated.
* https://www.freedesktop.org/software/systemd/man/sd_bus_detach_event.html
* https://www.freedesktop.org/software/systemd/man/sd_bus_close_unref.html
*/
if (*o->bus) {
r = sd_bus_detach_event(*o->bus);
if (r < 0)
return log_error(r, "sd_bus_detach_event()");
*o->bus = sd_bus_close_unref(*o->bus);
}
/* Set up a new bus object for the system bus, configure it to wait for D-Bus
* to be available instead of failing if it is not, and start it. All the
* following operations are asynchronous and will not block waiting for D-Bus
* to be available.
* https://www.freedesktop.org/software/systemd/man/sd_bus_new.html
* https://www.freedesktop.org/software/systemd/man/sd_bus_set_address.html
* https://www.freedesktop.org/software/systemd/man/sd_bus_set_bus_client.html
* https://www.freedesktop.org/software/systemd/man/sd_bus_negotiate_creds.html
* https://www.freedesktop.org/software/systemd/man/sd_bus_set_watch_bind.html
* https://www.freedesktop.org/software/systemd/man/sd_bus_set_connected_signal.html
* https://www.freedesktop.org/software/systemd/man/sd_bus_start.html
*/
r = sd_bus_new(o->bus);
if (r < 0)
return log_error(r, "sd_bus_new()");
r = sd_bus_set_address(*o->bus, "unix:path=/run/dbus/system_bus_socket");
if (r < 0)
return log_error(r, "sd_bus_set_address()");
r = sd_bus_set_bus_client(*o->bus, 1);
if (r < 0)
return log_error(r, "sd_bus_set_bus_client()");
r = sd_bus_negotiate_creds(*o->bus, 1, SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_EFFECTIVE_CAPS);
if (r < 0)
return log_error(r, "sd_bus_negotiate_creds()");
r = sd_bus_set_watch_bind(*o->bus, 1);
if (r < 0)
return log_error(r, "sd_bus_set_watch_bind()");
r = sd_bus_start(*o->bus);
if (r < 0)
return log_error(r, "sd_bus_start()");
/* Publish an interface on the bus, specifying our well-known object access
* path and public interface name.
* https://www.freedesktop.org/software/systemd/man/sd_bus_add_object.html
* https://dbus.freedesktop.org/doc/dbus-tutorial.html
*/
r = sd_bus_add_object_vtable(*o->bus,
NULL,
"/org/freedesktop/ReconnectExample",
"org.freedesktop.ReconnectExample",
vtable,
o);
if (r < 0)
return log_error(r, "sd_bus_add_object_vtable()");
/* By default, the service is only assigned an ephemeral name. Also add a
* well-known one, so that clients know whom to call. This needs to be
* asynchronous, as D-Bus might not be yet available. The callback will check
* whether the error is expected or not, in case it fails.
* https://www.freedesktop.org/software/systemd/man/sd_bus_request_name.html
*/
r = sd_bus_request_name_async(*o->bus,
NULL,
"org.freedesktop.ReconnectExample",
0,
request_name_callback,
o);
if (r < 0)
return log_error(r, "sd_bus_request_name_async()");
/* When D-Bus is disconnected this callback will be invoked, which will set up
* the connection again. This needs to be asynchronous, as D-Bus might not yet
* be available.
* https://www.freedesktop.org/software/systemd/man/sd_bus_match_signal_async.html
*/
r = sd_bus_match_signal_async(*o->bus,
NULL,
"org.freedesktop.DBus.Local",
NULL,
"org.freedesktop.DBus.Local",
"Disconnected",
on_disconnect,
NULL,
o);
if (r < 0)
return log_error(r, "sd_bus_match_signal_async()");
/* Attach the bus object to the event loop so that calls and signals are
* processed.
* https://www.freedesktop.org/software/systemd/man/sd_bus_attach_event.html
*/
r = sd_bus_attach_event(*o->bus, *o->event, 0);
if (r < 0)
return log_error(r, "sd_bus_attach_event()");
return 0;
}
int main(int argc, char **argv) {
/* The bus should be relinquished before the program terminates. The cleanup
* attribute allows us to do it nicely and cleanly whenever we exit the block.
*/
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
_cleanup_(sd_event_unrefp) sd_event *event = NULL;
object o = {
.example = "example",
.bus = &bus,
.event = &event,
};
int r;
/* Create an event loop data structure, with default parameters.
* https://www.freedesktop.org/software/systemd/man/sd_event_default.html
*/
r = sd_event_default(&event);
if (r < 0)
return log_error(r, "sd_event_default()");
/* By default, the event loop will terminate when all sources have disappeared,
* so we have to keep it 'occupied'. Register signal handling to do so.
* https://www.freedesktop.org/software/systemd/man/sd_event_add_signal.html
*/
r = sd_event_add_signal(event, NULL, SIGINT|SD_EVENT_SIGNAL_PROCMASK, NULL, NULL);
if (r < 0)
return log_error(r, "sd_event_add_signal(SIGINT)");
r = sd_event_add_signal(event, NULL, SIGTERM|SD_EVENT_SIGNAL_PROCMASK, NULL, NULL);
if (r < 0)
return log_error(r, "sd_event_add_signal(SIGTERM)");
r = setup(&o);
if (r < 0)
return EXIT_FAILURE;
/* Enter the main loop, it will exit only on sigint/sigterm.
* https://www.freedesktop.org/software/systemd/man/sd_event_loop.html
*/
r = sd_event_loop(event);
if (r < 0)
return log_error(r, "sd_event_loop()");
/* https://www.freedesktop.org/software/systemd/man/sd_bus_release_name.html */
r = sd_bus_release_name(bus, "org.freedesktop.ReconnectExample");
if (r < 0)
return log_error(r, "sd_bus_release_name()");
return 0;
}
اگرچه انتقال منابع از یک چرخهٔ راهاندازی مجدد نرم به چرخهٔ بعدی از این طریق امکانپذیر است، اما اکیداً پیشنهاد میکنیم از این قابلیت تنها بهندرت و با احتیاط استفاده شود، زیرا موجب ایجاد سیستمی شکنندهتر میشود چرا که منابع نسخههای مختلف سیستمعامل و برنامهها ممکن است با پیامدهای پیشبینینشده مخلوط شوند. بهویژه توصیه میشود از مجاز دانستن بقای فرایندها در طول عملیات راهاندازی مجدد نرم خودداری شود، زیرا این امر بدان معناست که بهروزرسانیهای کد لزوماً ناقص خواهند ماند و فرایندها معمولاً منابع مختلف دیگری را (مانند سیستم فایلی که بر آن متکی هستند) سنجاق (pin) میکنند، و در نتیجه مصرف حافظه را افزایش میدهند (چرا که ممکن است دو نسخه از سیستمعامل/برنامه/سیستم فایل در حافظه نگهداری شوند). در حال اجرا باقی گذاشتن فرایندها در طول یک عملیات راهاندازی مجدد نرم مستلزم قطع اتصال جامع و همهجانبهٔ سرویس از بقیهٔ سیستمعامل است، یعنی به حداقل رساندن IPC و کاهش اشتراکگذاری منابع با بقیهٔ سیستمعامل. یک سازوکار احتمالی برای دستیابی به این هدف، مفهوم Portable Services[1] است، اما اطمینان حاصل کنید که هیچ منبعی از سیستمهای فایل سیستمعامل میزبان از طریق BindPaths= یا تنظیمات مشابه واحد سنجاق نشده باشد، در غیر این صورت سیستم فایل قدیمی و مبدأ تا زمانی که واحد در حال اجرا باشد سوارشده باقی خواهد ماند.
نکات (NOTES)
توجه داشته باشید که از آنجا که systemd-shutdown(8) اجرا نمیشود، فایلهای اجرایی موجود در /usr/lib/systemd/system-shutdown/ نیز اجرا نخواهند شد.
توجه داشته باشید که systemd-soft-reboot.service (و واحدهای مرتبط) هرگز نباید بهصورت مستقیم اجرا شوند. در عوض، خاموشسازی سیستم را با دستوری مانند systemctl soft-reboot آغاز کنید.
توجه داشته باشید که اگر یک سیستم فایل ریشهٔ جدید در "/run/nextroot/" راهاندازی شده باشد، با فراخوانی دستور reboot یک عملیات soft-reboot انجام خواهد شد.
همچنین ببینید (SEE ALSO)
systemd(1), systemctl(1), systemd.special(7), systemd-poweroff.service(8), systemd-suspend.service(8), bootup(7)
نکات (NOTES)
- 1.
- Portable Services
| systemd 261.2 |