summaryrefslogtreecommitdiffstats
path: root/src (follow)
Commit message (Collapse)AuthorAgeFilesLines
* Add support for systemd-tpm2 libcryptsetup plugin.Ondrej Kozina2021-07-2610-0/+551
| | | | | | | | | | | Add support for systemd-tpm2 based LUKS2 device activation via libcryptsetup plugin. This make the feature (tpm2 sealed LUKS2 keyslot passphrase) usable from both systemd utilities and cryptsetup cli. The feature is configured via -Dlibcryptsetup-plugins combo with default value set to 'auto'. It get's enabled automatically when cryptsetup 2.4.0 or later is installed in build system.
* Merge pull request #20276 from keszybz/rpm-restart-wipLuca Boccassi2021-07-265-72/+214
|\ | | | | Reload user daemons and restart user services at the end of the rpm transaction
| * update-helper: also add "user-reexec" verbZbigniew Jędrzejewski-Szmek2021-07-241-1/+9
| | | | | | | | | | | | | | | | This is not called from the systemd.triggers or systemd.macros files. Instead, it would be called from the scriptlets in systemd rpm package itself, at the place where we call systemctl daemon-reexec. See https://github.com/systemd/systemd/pull/20289#issuecomment-885622200 .
| * rpm: restart user services at the end of the transactionZbigniew Jędrzejewski-Szmek2021-07-244-3/+91
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | This closes an important gap: so far we would reexecute the system manager and restart system services that were configured to do so, but we wouldn't do the same for user managers or user services. The scheme used for user managers is very similar to the system one, except that there can be multiple user managers running, so we query the system manager to get a list of them, and then tell each one to do the equivalent operations: daemon-reload, disable --now, set-property Markers=+needs-restart, reload-or-restart --marked. The total time that can be spend on this is bounded: we execute the commands in parallel over user managers and units, and additionally set SYSTEMD_BUS_TIMEOUT to a lower value (15 s by default). User managers should not have too many units running, and they should be able to do all those operations very quickly (<< 1s). The final restart operation may take longer, but it's done asynchronously, so we only wait for the queuing to happen. The advantage of doing this synchronously is that we can wait for each step to happen, and for example daemon-reloads can finish before we execute the service restarts, etc. We can also order various steps wrt. to the phases in the rpm transaction. When this was initially proposed, we discussed a more relaxed scheme with bus property notifications. Such an approach would be more complex because a bunch of infrastructure would have to be added to system manager to propagate appropriate notifications to the user managers, and then the user managers would have to wait for them. Instead, now there is no new code in the managers, all new functionality is contained in src/rpm/. The ability to call 'systemctl --user user@' makes this approach very easy. Also, it would be very hard to order the user manager steps and the rpm transaction steps. Note: 'systemctl --user disable' is only called for a user managers that are running. I don't see a nice way around this, and it shouldn't matter too much: we'll just leave a dangling symlink in the case where the user enabled the service manually. A follow-up for https://bugzilla.redhat.com/show_bug.cgi?id=1792468 and fa97d2fcf64e0558054bee673f734f523373b146.
| * rpm: call +needs-restart in parallelZbigniew Jędrzejewski-Szmek2021-07-241-1/+2
| | | | | | | | | | | | Some rpms install a bunch of units… It seems nicer to invoke them all in parallel. In particular, timeouts in systemctl also run in parallel, so if there's some communication mishap, we will wait less.
| * rpm: use a helper script to actually invoke systemctl commandsZbigniew Jędrzejewski-Szmek2021-07-245-57/+102
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Instead of embedding the commands to invoke directly in the macros, let's use a helper script as indirection. This has a couple of advantages: - the macro language is awkward, we need to suffix most commands by "|| :" and "\", which is easy to get wrong. In the new scheme, the macro becomes a single simple command. - in the script we can use normal syntax highlighting, shellcheck, etc. - it's also easier to test the invoked commands by invoking the helper manually. - most importantly, the logic is contained in the helper, i.e. we can update systemd rpm and everything uses the new helper. Before, we would have to rebuild all packages to update the macro definition. This raises the question whether it makes sense to use the lua scriptlets when the real work is done in a bash script. I think it's OK: we still have the efficient lua scripts that do the short scripts, and we use a single shared implementation in bash to do the more complex stuff. The meson version is raised to 0.47 because that's needed for install_mode. We were planning to raise the required version anyway…
| * rpm: don't specify the full path for systemctl and other commandsZbigniew Jędrzejewski-Szmek2021-07-213-30/+30
| | | | | | | | | | | | | | | | | | We can make things a bit simpler and more readable by not specifying the path. Since we didn't specify the full path for all commands (including those invoked recursively by anythign we invoke), this didn't really privide any security or robustness benefits. I guess that full paths were used because this style of rpm packagnig was popular in the past, with macros used for everything possible, with special macros for common commands like %{__ln} and %{__mkdir}.
* | Merge pull request #20288 from keszybz/freeze-no-mallocLuca Boccassi2021-07-2311-204/+179
|\ \ | | | | | | Don't call malloc from freeze which is called in a signal handler
| * | Add variant of close_all_fds() that does not allocate and use it in freeze()Zbigniew Jędrzejewski-Szmek2021-07-233-6/+9
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Even though it's just a fallback path, let's not be sloppy and allocate in the crash handler. > The deadlock happens because systemd crash in malloc() then in signal > handler, it calls malloc() (close_all_fds()-> opendir()-> __alloc_dir()) > again. malloc() is not a signal-safe function, maybe we should re-think > the logic here. Fixes #20266.
| * | Move freeze() into shared/Zbigniew Jędrzejewski-Szmek2021-07-236-29/+28
| | | | | | | | | | | | | | | | | | | | | | | | Library code should not call freeze(), this is something that should only be done by "application code", so moving it into shared/ is appropriate. The fallback to call _exit() is dropped: let's trust that the infinite loop is infinite.
| * | Move fork_agent() into shared/Zbigniew Jędrzejewski-Szmek2021-07-236-78/+77
| | | | | | | | | | | | | | | | | | | | | Currently it's only used in two places in src/shared/, so the function was already included just once in compiled code. But it seems appropriate to move it there anyway, because library code should have no need to fork agents, so it doesn't belong in basic/.
| * | basic/process-util: use xsprintf() in one more placeZbigniew Jędrzejewski-Szmek2021-07-231-1/+1
| | |
| * | Make oom_score_adjust_is_valid() staticZbigniew Jędrzejewski-Szmek2021-07-233-6/+5
| | | | | | | | | | | | It has only one user and we don't need to put it in basic/.
| * | basic/fd-util: sort the 'except' array in placeZbigniew Jędrzejewski-Szmek2021-07-234-91/+66
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | We need a sorted list of fds to skip over when closing. We would allocate a copy of the passed array to do the sort. But all callers construct a temporary array to pass to us, so it is pointless to copy it again. close_all_fds/safe_fork_full/namespace_fork/fork_agent are changed to pass a non-const int array. I checked all users, and all callers are fine with the array being sorted. The function was returning some number (sometimes 1, sometimes the extent of the range passed over to close_range(), ???). Anyway, all callers only check for error, so let's return 0 on success.
* | | discover-image: mount as read-only when extracting metadataLuca Boccassi2021-07-231-0/+1
| | | | | | | | | | | | We don't need to modify the image, and the loopback device is already set to read-only.
* | | malloc() uses getrandom nowCristian Rodríguez2021-07-231-0/+1
| | | | | | | | | glibc master uses getrandom in malloc since https://sourceware.org/git/?p=glibc.git;a=commit;h=fc859c304898a5ec72e0ba5269ed136ed0ea10e1 , getrandom should be in the default set so to avoid all non trivial programs to fallback to a PRNG.
* | | logind: action* parameters can't be NULL in verify_shutdown_creds()Franck Bui2021-07-231-9/+10
|/ / | | | | | | | | | | | | "action", "action_multiple_sessions" and "action_ignore_inhibit" can't be NULL in practice so let's simplify a bit the code. No functional change.
* | Merge pull request #20273 from keszybz/extended-job-statusLuca Boccassi2021-07-221-89/+137
|\ \ | | | | | | Propagate the original command line when reexecuting PID1
| * | pid1: propagate the original command line when reexecutingZbigniew Jędrzejewski-Szmek2021-07-221-10/+53
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | When we reexec the manager in a container, we lose configuration settings on the kernel command line: $ systemd-nspawn -M rawhide -b systemd.status-unit-format=name systemd.show-status=yes ... # tr '\0' ' ' </proc/1/cmdline /usr/lib/systemd/systemd systemd.status_unit_format=combined systemd.show-status=yes # sudo systemctl daemon-reexec # tr '\0' ' ' </proc/1/cmdline /usr/lib/systemd/systemd --system --deserialize 20 This means that after daemon-reexec, the settings that we gain from the commandline are reset to defaults. So let's reeexecute with the original arguments copied over, modulo some filtering.
| * | core/main: wrap long comment linesZbigniew Jędrzejewski-Szmek2021-07-191-79/+84
| | |
* | | Merge pull request #20123 from keszybz/extended-job-statusLuca Boccassi2021-07-2116-720/+801
|\| | | | | | | | Nested job status for systemd+user service managers
| * | manager: print status text of the service when waiting for a jobZbigniew Jędrzejewski-Szmek2021-07-191-13/+52
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | This does two semi-independent but interleaved things: firstly, the manager now prints the status text (if available) of a service when we have a job running for that service and it is slow. Because it's hard to fit enough info on the line, we only do this if the output mode uses unit names. The format of the line "… job is running for …" is changed to be shorter. This way we can somewhat reasonably fit two status messages on one line. Secondly, the manager now sends more information using sd_notify. This mostly matters for in case of the user manager. In particular, we notify when starting one of the special units. Without this, when the system manager would display a line about waiting for the user manager, it would show status like "Ready.", which is confusing. Now it'll either show something like "Started special unit shutdown.target", or the line about waiting for a user job. Also, the timeouts for the user manager are lowered: the user manager usually (always?) has status disabled, so we would wait for 25 seconds before showing job progress. Normally we don't expect to have any jobs that take more than a second. So let's start the progress output fairly quickly, like we would if status showing was enabled. This obviously makes the output in the system manager about the user manager more useful. The timeouts are "desynchronized" by a fraction so if there are multiple jobs running, we'll cycle through showing all combinations. Example output: Stopping user@1000.service... [ OK ] Stopped dracut-shutdown.service. [ OK ] Stopped systemd-logind.service. [ OK ] Stopped systemd-logind.service - User Login Management. [* ] Job user@1000.service/stop running (2s / 2min): (1 of 2) User job slowstop.service/stop running (1s / 1min 30s)... [*** ] Job user@1000.service/stop running (3s / 2min): (2 of 2) User job slowstop2.service/stop running (2s / 1min 30s)... [ ***] Job user@1000.service/stop running (4s / 2min): (1 of 2) User job slowstop.service/stop running (4s / 1min 30s)... [ *] Job user@1000.service/stop running (5s / 2min): (1 of 2) User job slowstop.service/stop running (5s / 1min 30s)... [ ***] Job user@1000.service/stop running (6s / 2min): (2 of 2) User job slowstop2.service/stop running (6s / 1min 30s)... [*** ] Job user@1000.service/stop running (8s / 2min): (1 of 2) User job slowstop.service/stop running (7s / 1min 30s)... [*** ] Job user@1000.service/stop running (10s / 2min): (2 of 2) User job slowstop2.service/stop running (9s / 1min 30s)... [ *** ] Job user@1000.service/stop running (11s / 2min): (1 of 2) User job slowstop.service/stop running (10s / 1min 30s)... [ *] Job user@1000.service/stop running (12s / 2min): (2 of 2) User job slowstop2.service/stop running (12s / 1min 30s)... [ ***] Job user@1000.service/stop running (13s / 2min): (1 of 2) User job slowstop.service/stop running (13s / 1min 30s)... [*** ] Job user@1000.service/stop running (15s / 2min): (2 of 2) User job slowstop2.service/stop running (14s / 1min 30s)... [* ] Job user@1000.service/stop running (15s / 2min): (2 of 2) User job slowstop2.service/stop running (14s / 1min 30s)... [*** ] Job user@1000.service/stop running (16s / 2min): User job slowstop.service/stop running (16s / 1min 30s)... [ ***] Job user@1000.service/stop running (18s / 2min): User job slowstop.service/stop running (17s / 1min 30s)... [ *] Job user@1000.service/stop running (19s / 2min): User job slowstop.service/stop running (18s / 1min 30s)... [ ***] Job user@1000.service/stop running (20s / 2min): User job slowstop.service/stop running (19s / 1min 30s)... [* ] Job user@1000.service/stop running (22s / 2min): User job slowstop.service/stop running (22s / 1min 30s)... [** ] Job user@1000.service/stop running (30s / 2min): User job slowstop.service/stop running (29s / 1min 30s)... [ ***] Job user@1000.service/stop running (32s / 2min): User job slowstop.service/stop running (31s / 1min 30s)... [ *] Job user@1000.service/stop running (33s / 2min): User job slowstop.service/stop running (32s / 1min 30s)... [ ***] Job user@1000.service/stop running (34s / 2min): User job slowstop.service/stop running (33s / 1min 30s)... [** ] Job user@1000.service/stop running (37s / 2min): User job slowstop.service/stop running (36s / 1min 30s)... [ *** ] Job user@1000.service/stop running (41s / 2min): User job slowstop.service/stop running (41s / 1min 30s)... [ OK ] Stopped user@1000.service - User Manager for UID 1000. Stopping user-runtime-dir@1000.service - User Runtime Directory /run/user/1000... [ OK ] Unmounted run-user-1000.mount - /run/user/1000. [ OK ] Stopped user-runtime-dir@1000.service - User Runtime Directory /run/user/1000. If the output width is lower than approximately 100 columns, the output stops being very useful. No idea what to do about that.
| * | manager: rework sending of STATUS=Zbigniew Jędrzejewski-Szmek2021-07-192-14/+23
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | We would send READY=1,STATUS="Startup finished in …" once after finishing boot. This changes the message to just "Ready.". The time used to reach readiness is not part of the ongoing status — it's just a bit of debug information that it useful in some scenarious, but completely uninteresting most of the time. Also, when we start sending status about other things in subsequent patches, we can't really go back to showing "Startup finished in …" later on. So let's just show "Ready." whenever we're in the steady state. In manager_check_finished(), more steps are skipped if MANAGER_IS_FINISHED(). Those steps are idempotent, but no need to waste cycles trying to do them more than once. We'll now also check whether to send the status message whenever the job queue runs empty. If we already sent the exact same message already, we'll not send again.
| * | manager: always log when starting a "special unit"Zbigniew Jędrzejewski-Szmek2021-07-191-18/+14
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | This is the initiatation of the machine shutdown/reboot/etc, so it's useful to log about this. We log about the steps that we take, but so far we didn't really log why we started the sequence (except at debug level). The function is renamed, because we also use it for dbus.service, not just targets.
| * | core: add helper to retrieve service.status_textZbigniew Jędrzejewski-Szmek2021-07-192-0/+18
| | |
| * | core: align string tablesZbigniew Jędrzejewski-Szmek2021-07-1910-134/+134
| | |
| * | core: modernize asprintf error handlingZbigniew Jędrzejewski-Szmek2021-07-191-9/+4
| | | | | | | | | | | | | | | | | | The man page says asprintf() pointer is "undefined" on error, but the only meaningful interpretation is that it's either NULL or points to something that should be freed with free().
| * | core: split out manager-serialize.[ch]Zbigniew Jędrzejewski-Szmek2021-07-196-533/+557
| | | | | | | | | | | | The file is super long, so let's split this out one subject to a new file.
* | | sd-bus: fix missing initializer in SD_BUS_VTABLE_END (#20253)Matthijs van Duin2021-07-211-1/+8
| |/ |/| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | When two fields were added to the vtable.x.start struct, no initializers for these were added to SD_BUS_VTABLE_END which also (ab)used that struct (albeit sneakily by using non-designated initialization). While C tolerates this, C++ prohibits these missing initializers, and both g++ and clang++ will complain when using -Wextra. This patch gives SD_BUS_VTABLE_END its own case in the union and clarifies its initialization. I tested the behaviour of g++ 10.2 and clang 11 in various cases. Both will warn (-Wmissing-field-initializers, implied by -Wextra) if you provide initializers for some but not all fields of a struct. Declaring x.end as empty struct or using an empty initializer {} to initialize the union or one of its members is valid C++ but not C, although both gcc and clang accept it without warning (even at -Wall -Wextra -std=c90/c++11) unless you use -pedantic (which requires -std=c99/c++2a to support designated initializers). Interestingly, .x = { .start = { 0, 0, NULL } } is the only initializer I found for the union (among candidates for SD_BUS_VTABLE_END) where gcc doesn't zero-fill it entirely when allocated on stack, it looked like it did in all other cases (I only examined this on 32-bit arm). clang always seems to initialize all bytes of the union. [zjs: test case: $ cat vtable-test.cc #include "sd-bus.h" const sd_bus_vtable vtable[] = { SD_BUS_VTABLE_END }; $ g++ -I src/systemd/ -Wall -Wmissing-field-initializers -c vtable-test.cc vtable-test.cc:5:1: warning: missing initializer for member ‘sd_bus_vtable::<unnamed union>::<unnamed struct>::features’ [-Wmissing-field-initializers] 5 | }; | ^ vtable-test.cc:5:1: warning: missing initializer for member ‘sd_bus_vtable::<unnamed union>::<unnamed struct>::vtable_format_reference’ [-Wmissing-field-initializers] $ clang++ -I src/systemd/ -Wmissing-field-initializers -c vtable-test.cc vtable-test.cc:4:4: warning: missing field 'features' initializer [-Wmissing-field-initializers] SD_BUS_VTABLE_END ^ src/systemd/sd-bus-vtable.h:188:28: note: expanded from macro 'SD_BUS_VTABLE_END' .x = { { 0 } }, \ ^ 1 warning generated. Both warnings are gone with the patch.]
* | Merge pull request #20087 from xen0n/loongarch64-gptZbigniew Jędrzejewski-Szmek2021-07-212-85/+105
|\ \ | | | | | | gpt: support LoongArch 64-bit
| * | gpt: reformat for restoring vertical alignmentWANG Xuerui2021-07-202-87/+87
| | |
| * | gpt: support LoongArch 64-bitWANG Xuerui2021-07-202-2/+22
| | |
* | | Merge pull request #20256 from keszybz/one-alloca-too-manyZbigniew Jędrzejewski-Szmek2021-07-201-10/+7
|\ \ \ | |/ / |/| | basic/unit-name: do not use strdupa() on a path
| * | basic/unit-name: adjust commentsZbigniew Jędrzejewski-Szmek2021-06-231-2/+2
| | | | | | | | | | | | We already checked for "too long" right above…
| * | basic/unit-name: do not use strdupa() on a pathZbigniew Jędrzejewski-Szmek2021-06-231-8/+5
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | The path may have unbounded length, for example through a fuse mount. CVE-2021-33910: attacked controlled alloca() leads to crash in systemd and ultimately a kernel panic. Systemd parses the content of /proc/self/mountinfo and each mountpoint is passed to mount_setup_unit(), which calls unit_name_path_escape() underneath. A local attacker who is able to mount a filesystem with a very long path can crash systemd and the whole system. https://bugzilla.redhat.com/show_bug.cgi?id=1970887 The resulting string length is bounded by UNIT_NAME_MAX, which is 256. But we can't easily check the length after simplification before doing the simplification, which in turns uses a copy of the string we can write to. So we can't reject paths that are too long before doing the duplication. Hence the most obvious solution is to switch back to strdup(), as before 7410616cd9dbbec97cf98d75324da5cda2b2f7a2.
* | | Merge pull request #20251 from keszybz/test-format-lifetimeYu Watanabe2021-07-195-17/+47
|\ \ \ | | | | | | | | Add test for format_lifetime() and fix prefix
| * | | basic/time-util: inline one more variable declarationZbigniew Jędrzejewski-Szmek2021-07-191-5/+3
| | | |
| * | | networkd: fix and simplify format_lifetime()Zbigniew Jędrzejewski-Szmek2021-07-194-12/+44
| | |/ | |/| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | We would copy "forever" into the buffer. This is a fairly common case, so let's do a microoptimization and return a static string. (All callers use the return pointer, so this works just as well.) The prefix "for " was not displayed, because the pointer to the part of the buffer after "for " was returned. (Maybe it's just me, but I find strpcpy() and associated functions really hard to use… I always have to look up what the do exactly and what the return value is.) A simple test is added.
* | | log-generator: count arguments as offset from an iteratormonosans2021-07-191-12/+4
| | |
* | | udev-event: drop unused assignmentsZbigniew Jędrzejewski-Szmek2021-07-191-20/+20
| | | | | | | | | | | | | | | clang's static analyzer reports: Value stored to 'l' is never read
* | | network: configure address with requested lifetimeYu Watanabe2021-07-191-5/+5
| | | | | | | | | | | | | | | | | | | | | | | | | | | When assigning the same address provided by a dynamic addressing protocol, the new lifetime is stored on Request::Address, but not Address object in Link object, which can be obtained by address_get(). So, we need to configure address with Address object in Request. Fixes #20245.
* | | Add meson option to disable urlify.James Hilliard2021-07-191-0/+4
|/ / | | | | | | | | Useful for systems that don't use a version of less with hyperlink support.
* | tree-wide: FORMAT_TIMESTAMP() or friends must be used as a function argumentYu Watanabe2021-07-155-93/+64
| | | | | | | | Follow-ups for #20109.
* | network: slightly simplify log_address_debug()Yu Watanabe2021-07-151-5/+2
| |
* | network: introduce FORMAT_LIFETIME()Yu Watanabe2021-07-153-29/+35
| | | | | | | | | | | | Fixes a bug introduced by 5291f26d4a6450d1fbf3656640ef20c5e78aa6a5. Fixes #20227.
* | network: dhcp4: also support semi-static routes with Gateway=_dhcp4 when ↵Yu Watanabe2021-07-141-11/+80
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | UseGateway=no or UseRoutes=no This makes the default gateway is read from classless static routes or router option even if UseGateway=no or UseRoutes=no, and will be used when configuring semi-static routes such that specified with Gateway=_dhcp4. This also changes the behavior of RoutesToDNS= or RoutesToNTP=. Previously, the DNS or NTP servers are not in the same network, then the routes to the servers were not configured when UseGateway=no or UseRoutes=no. With this commit, the default gateway in classless static routes or router option will used to connecting the servers even if UseGateway=no or UseRoutes=no. Fixes #20208.
* | network: further unification of MUD url parsersYu Watanabe2021-07-146-79/+16
| | | | | | | | Follow-up for 89fa9a6b7b2505aa2ce18febf1e28e79510dfec2.
* | Merge pull request #20109 from keszybz/timestamp-macrosYu Watanabe2021-07-1498-954/+681
|\ \ | | | | | | Add macros that define scratch buffer internally for timestamp/timespan formatting
| * | networkd: minor refactoringZbigniew Jędrzejewski-Szmek2021-07-091-9/+6
| | |
| * | networkd: replace one trivial asprintf with xsprintfZbigniew Jędrzejewski-Szmek2021-07-091-3/+5
| | |