SYSTEMD-SOFT-REBOOT.SERVICE(8)

الاسم
systemd-soft-reboot.service - عملية إعادة تشغيل فضاء المستخدم

موجز

systemd-soft-reboot.service

الوصف

systemd-soft-reboot.service هي خدمة نظام تُستجلب بواسطة soft-reboot.target وتكون مسؤولة عن تنفيذ عملية إعادة تشغيل خاصة بفضاء المستخدم فقط. عند استدعائها، تُرسل إشارة SIGTERM إلى أي عمليات لا تزال قيد التشغيل (لكنها لا تنتظر خروج العمليات)، وتتبعها بـ SIGKILL. إذا كان الدليل /run/nextroot/ موجودًا (والذي قد يكون دليلًا عاديًا، أو نقطة وصل دليل، أو رابطًا رمزيًا لأي منهما)، فستُحوِّل جذر نظام ملفات إليه. ثم تُعيد تنفيذ مدير الخدمات من نظام ملفات الجذر (ربما الجديد الآن)، مما سيُدرج معاملة إقلاع جديدة كما في إعادة التشغيل العادية.

تسمح عملية إعادة التشغيل الخاصة بفضاء المستخدم فقط هذه بتحديث أو إعادة تعيين كامل فضاء المستخدم مع أقل وقت تعطل، لأن عملية إعادة التشغيل لا تمر عبر:

    • المرحلة الثانية من الإغلاق العادي، كما تُنفذ بواسطة systemd-shutdown(8).

    • المرحلة الثالثة من الإغلاق العادي، أي العودة إلى سياق initrd.

    • عملية إعادة تشغيل العتاد.

    • تهيئة البرامج الثابتة.

    • تهيئة محمل الإقلاع.

    • تهيئة النواة.

    • تهيئة initrd.

ومع ذلك، يأتي هذا الشكل من إعادة التشغيل مع عيوب أيضًا:

    • يبقى تحديث نظام التشغيل غير مكتمل، لأن النواة لا تُعاد تعيينها وتستمر في التشغيل.

    • إعدادات النواة (مثل إعدادات /proc/sys/، المعروفة أيضًا باسم "sysctl"، أو إعدادات /sys/) لا تُعاد تعيينها.

يمكن معالجة هذه القيود بوسائل متنوعة، وهي خارج نطاق هذه الوثائق، مثل التصحيح المباشر للنواة وملفات /etc/sysctl.d/ الشاملة بما يكفي.

تمرير الموارد

يمكن تمرير موارد متنوعة لنظام التشغيل في وقت التشغيل من جلسة نظام إلى التالية، عبر عملية إعادة تشغيل فضاء المستخدم. تحديدًا:

    • تُمرَّر واصفات الملفات الموضوعة في مخزن واصفات الملفات للخدمات التي تبقى نشطة حتى النهاية إلى الإقلاع التالي، حيث تُوضع في مخزن واصفات الملفات لنفس الوحدة. لكي يعمل هذا، يجب على الوحدات أن تُعلن DefaultDependencies=no (وتتجنب Conflicts=shutdown.target يدويًا أو ما شابه) لضمان عدم إنهائها كالمعتاد أثناء عملية إغلاق النظام. بدلاً من ذلك، استخدم FileDescriptorStorePreserve= للسماح لمخزن واصفات الملفات بالبقاء مثبتًا حتى عندما تكون الوحدة معطلة. انظر systemd.service(5) للحصول على تفاصيل حول مخزن واصفات الملفات.

    • على غرار هذا، تبقى واصفات الملفات المرتبطة بوحدات .socket مفتوحة (وقابلة للاتصال) إذا لم تُوقف الوحدات أثناء الانتقال. (يُحقق ذلك بواسطة DefaultDependencies=no.)

    • يبقى نظام ملفات /run/ موصولاً ومملوءًا ويمكن استخدامه لتمرير معلومات الحالة بين دورات إعادة تشغيل فضاء المستخدم هذه.

    • يمكن لعمليات الخدمة أن تستمر في التشغيل عبر الانتقال، بعد إعادة التشغيل الناعمة وإلى الجلسة التالية، إذا وُضعت في خدمات تبقى نشطة حتى نهاية الإغلاق (والذي يُحقق مرة أخرى عبر DefaultDependencies=no). يجب أيضًا إعدادها لتجنب القتل بواسطة SIGTERM و SIGKILL المذكورين عبر SurviveFinalKillSignal=yes، وتكوينها أيضًا لتجنب الإيقاف عند العزل عبر IgnoreOnIsolate=yes. يجب أيضًا تكوينها للإيقاف عند الإغلاق العادي، وإعادة التشغيل، ووضع الصيانة. أخيرًا، يجب ترتيبها بعد basic.target لضمان الترتيب الصحيح عند الإقلاع. لاحظ أنه في حالة استخدام أي وحدات جديدة أو مخصصة للعزل إليها، أو التي تنفذ وظيفة إغلاق مكافئة، يجب أيضًا تكوينها يدويًا للترتيب الصحيح والتعارض. على سبيل المثال:

        [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

    • علاوة على ما سبق، تحتاج الوحدات المقولبة أيضًا إلى ملف تكوين لشريحتها، لأنها تستخدم مبدئيًا شريحة مسماة باسم الجزء غير المقولب من الوحدة. على سبيل المثال، لمثيل foo@test.service، يمكن إضافة وحدة system-foo.slice بالمحتوى التالي:

        [Unit]
        SurviveFinalKillSignal=yes
        IgnoreOnIsolate=yes
        DefaultDependencies=no

    • قد تبقى عمليات وصل نظام ملفات موصولة أثناء الانتقال، والتخزين المعقد ملحقًا، إذا ضُبطت للبقاء حتى نهاية عملية الإيقاف. (يُحقق أيضًا عبر DefaultDependencies=no، وتجنب Conflicts=umount.target)

    • إذا نشرت الوحدة خدمة عبر D-Bus، يجب إعادة إنشاء الاتصال بعد إعادة التشغيل الناعمة حيث سيُوقف وسيط D-Bus ثم يُشغّل مرة أخرى. عند استخدام مكتبة sd-bus(3) يمكن تحقيق ذلك بتكييف المثال التالي.

        /* SPDX-License-Identifier: MIT-0 */

        /* خدمة D-Bus تُعيد الاتصال آليًا عند إعادة تشغيل ناقل النظام.
         *
         * جمّع باستخدام 'cc sd_bus_service_reconnect.c $(pkg-config --libs --cflags libsystemd)'
         *
         * للسماح للبرنامج بامتلاك الاسم 'org.freedesktop.ReconnectExample'،
         * أضف ما يلي كـ /etc/dbus-1/system.d/org.freedesktop.ReconnectExample.conf
         * ثم أعد تحميل الوسيط باستخدام '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 فشل: %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,
                                   "خاصية مجهولة '%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;
        }

        /* التأكد من خروج حلقة الأحداث بخطأ واضح إذا فشل الحصول على
         * اسم الخدمة المعروف */
        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; /* الناقل غير متاح، حاول لاحقًا */

          fprintf(stderr, "فشل طلب الاسم: %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;

          /* إذا كنا نُعيد الاتصال، فيجب إغلاق كائن الناقل وفصله
           * من حلقة الأحداث وإعادة إنشائه.
           * 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);
          }

          /* أنشئ كائن ناقل جديد لناقل النظام، واضبطه للانتظار حتى يكون
           * D-Bus متاحًا بدلاً من الفشل، ثم ابدأ تشغيله. كل العمليات
           * التالية غير متزامنة ولن تمنع الانتظار ليكون D-Bus
           * متاحًا.
           * 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()");

          /* انشر واجهة على الناقل، مع تحديد مسار وصول الكائن
           * المعروف واسم الواجهة العامة.
           * 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()");
          /* يُسند للخدمة اسم عابر مبدئيًا. أضف أيضًا اسمًا
           * معروفًا، ليعرف العملاء من يستدعون. يجب أن يكون هذا
           * غير متزامن، لأن D-Bus قد لا يكون متاحًا بعد. سيتحقق الاستدعاء الراجع
           * مما إذا كان الخطأ متوقعًا أم لا، في حال فشله.
           * 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()");
          /* عند قطع اتصال D-Bus سيُستدعى هذا الاستدعاء الراجع، والذي سيُعد
           * الاتصال مرة أخرى. يجب أن يكون هذا غير متزامن، لأن D-Bus قد لا
           * يكون متاحًا بعد.
           * 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()");
          /* ألحق كائن الناقل بحلقة الأحداث لتُعالج الاستدعاءات والإشارات.
           * 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) {
          /* يجب التخلي عن الناقل قبل انتهاء البرنامج. تسمح لنا
           * سمة التنظيف بالقيام بذلك بشكل جميل ونظيف كلما خرجنا من الكتلة.
           */
          _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;

          /* أنشئ بنية بيانات لحلقة الأحداث، بمعاملات مبدئية.
           * 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()");

          /* ستنتهي حلقة الأحداث مبدئيًا عند اختفاء جميع المصادر،
           * لذا يجب علينا إبقاؤها 'مشغولة'. سجّل معالجة الإشارات للقيام بذلك.
           * 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;

          /* ادخل الحلقة الرئيسة، ولن تخرج إلا عند 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;
        }

على الرغم من أن تمرير الموارد من دورة إعادة تشغيل ناعمة إلى التالية ممكن بهذه الطريقة، إلا أننا نقترح بشدة استخدام هذه الوظيفة باعتدال فقط، لأنها تخلق نظامًا أكثر هشاشة حيث قد تُخلط الموارد من إصدارات مختلفة من نظام التشغيل والتطبيقات مع عواقب غير متوقعة. يوصى بشكل خاص بـ تجنب السماح للعمليات بالبقاء حية بعد عملية إعادة التشغيل الناعمة، لأن هذا يعني أن تحديثات الكود ستكون بالضرورة غير مكتملة، وعادة ما تُثبّت العمليات موارد أخرى متنوعة (مثل نظام ملفات الذي تستند إليه)، مما يزيد من استهلاك الذاكرة (حيث قد يَبقى إصداران من نظام التشغيل/التطبيق/نظام الملفات في الذاكرة). يتطلب ترك العمليات قيد التشغيل أثناء عملية إعادة التشغيل الناعمة فصل الخدمة بشكل شامل عن بقية نظام التشغيل، أي تقليل IPC وتقليل مشاركة الموارد مع بقية نظام التشغيل. الآلية الممكنة لتحقيق ذلك هي مفهوم الخدمات المحمولة[1]، ولكن تأكد من عدم تثبيت أي مورد من أنظمة ملفات نظام تشغيل المضيف عبر BindPaths= أو إعدادات وحدة مماثلة، وإلا سيبقى نظام الملفات القديم الأصلي موصولاً طالما أن الوحدة قيد التشغيل.

ملاحظات

لاحظ أنه نظرًا لعدم تنفيذ systemd-shutdown(8)، فلن تُنفذ الملفات التنفيذية في /usr/lib/systemd/system-shutdown/ أيضًا.

لاحظ أنه يجب عدم تنفيذ systemd-soft-reboot.service (والوحدات ذات الصلة) مباشرة أبدًا. بدلاً من ذلك، استحث إغلاق النظام بأمر مثل systemctl soft-reboot.

لاحظ أنه إذا ضُبط نظام ملفات جذر جديد على "/run/nextroot/"، فستُجرى إعادة تشغيل ناعمة (soft-reboot) عند استدعاء الأمر reboot.

انظر أيضًا

systemd(1)، systemctl(1)، systemd.special(7)، systemd-poweroff.service(8)، systemd-suspend.service(8)، bootup(7)

ملاحظات

1.  الخدمات المحمولة
    https://systemd.io/PORTABLE_SERVICES

ترجمة
تُرجمت هذه الصفحة من الدليل بواسطة زايد السعيدي <zayed.alsaidi@gmail.com>

هذه الترجمة هي وثيقة مجانية؛ راجع رخصة جنو العامة الإصدار 3 (https://www.gnu.org/licenses/gpl-3.0.html) أو ما بعده للاطلاع على شروط حقوق النشر. لا توجد أي ضمانات.

إذا وجدت أي أخطاء في ترجمة صفحة الدليل هذه، يرجى إرسال بريد إلكتروني إلى قائمة بريد المترجمين: kde-l10n-ar@kde.org.