*.a
*.cache
-*.html
+*.gch
*.la
*.lo
*.log
/exported
/exported-*
/hostnamectl
+/image.raw
/install-tree
/journalctl
/libtool
/systemd-machine-id-setup
/systemd-machined
/systemd-modules-load
+/systemd-mount
/systemd-networkd
/systemd-networkd-wait-online
/systemd-notify
/systemd-rc-local-generator
/systemd-remount-fs
/systemd-reply-password
-/systemd-resolve-host
+/systemd-resolve
/systemd-resolved
/systemd-rfkill
/systemd-run
/systemd-vconsole-setup
/tags
/test-acd
+/test-acl-util
/test-af-list
/test-alloc-util
/test-architecture
/test-arphrd-list
+/test-ask-password-api
/test-async
/test-audit-type
/test-barrier
/test-bus-policy
/test-bus-server
/test-bus-signature
+/test-bus-track
/test-bus-zero-copy
/test-calendarspec
/test-cap-list
/test-daemon
/test-date
/test-device-nodes
+/test-dnssec-complex
/test-dhcp-client
/test-dhcp-option
/test-dhcp-server
/test-dhcp6-client
/test-dns-domain
+/test-dns-packet
+/test-dnssec
/test-efi-disk.img
/test-ellipsize
/test-engine
-/test-env-replace
+/test-env-util
+/test-escape
/test-event
/test-execute
/test-extract-word
+/test-fd-util
/test-fdset
/test-fileio
/test-firewall-util
+/test-fs-util
/test-fstab-util
+/test-glob-util
/test-hashmap
+/test-hexdecoct
/test-hostname
/test-hostname-util
/test-id128
/test-inhibit
/test-install
/test-install-root
+/test-io-util
/test-ipcrm
/test-ipv4ll
/test-ipv4ll-manual
/test-ndisc-rs
/test-netlink
/test-netlink-manual
+/test-networkd-conf
/test-network
/test-network-tables
/test-ns
+/test-nss
/test-parse-util
+/test-patch-uid
/test-path
/test-path-lookup
/test-path-util
/test-prioq
+/test-proc-cmdline
/test-process-util
/test-pty
/test-qcow2
/test-ratelimit
/test-replace-var
/test-resolve
+/test-resolve-tables
/test-ring
+/test-rlimit-util
/test-sched-prio
+/test-seccomp
+/test-selinux
/test-set
+/test-sizeof
/test-sigbus
+/test-signal-util
/test-siphash24
/test-sleep
/test-socket-util
-/test-ssd
+/test-stat-util
/test-strbuf
/test-string-util
/test-strip-tab-ansi
Michael Olbrich <m.olbrich@pengutronix.de>
Douglas Christman <DouglasChristman@gmail.com>
Alexander Kuleshov <kuleshovmail@gmail.com> <0xAX@users.noreply.github.com>
+Andreas Henriksson <andreas@fatal.se>
+Daniel Rusek <mail@asciiwolf.com>
+Dennis Wassenberg <dennis.wassenberg@secunet.com>
+Reid Price <reid.price@gmail.com>
+Stefan Schweter <stefan@schweter.it>
+Seraphime Kirkovski <kirkseraph@gmail.com>
+Bart Rulon <barron@lexmark.com>
+Richard W.M. Jones <rjones@redhat.com>
+Roman Stingler <coolx67@gmx.at>
+Michael Hoy <rimmington@gmail.com>
+Tiago Levit <liamgliam@gmail.com>
+Eric Cook <llua@users.noreply.github.com>
+Lukáš Nykrýn <lnykryn@redhat.com>
+Heikki Kemppainen <heikki.kemppainen@nokia.com>
+Hendrik Brueckner <hbrueckner@users.noreply.github.com>
man/sd_event_source_unrefp.3 \
man/sd_id128_from_string.3 \
man/sd_id128_get_boot.3 \
+ man/sd_id128_get_invocation.3 \
man/sd_machine_get_ifindices.3 \
man/sd_notifyf.3 \
man/sd_pid_notify.3 \
man/sd_event_source_unrefp.3: man/sd_event_source_unref.3
man/sd_id128_from_string.3: man/sd_id128_to_string.3
man/sd_id128_get_boot.3: man/sd_id128_get_machine.3
+man/sd_id128_get_invocation.3: man/sd_id128_get_machine.3
man/sd_machine_get_ifindices.3: man/sd_machine_get_class.3
man/sd_notifyf.3: man/sd_notify.3
man/sd_pid_notify.3: man/sd_notify.3
man/sd_id128_get_boot.html: man/sd_id128_get_machine.html
$(html-alias)
+man/sd_id128_get_invocation.html: man/sd_id128_get_machine.html
+ $(html-alias)
+
man/sd_machine_get_ifindices.html: man/sd_machine_get_class.html
$(html-alias)
# Keep the test-suite.log
.PRECIOUS: $(TEST_SUITE_LOG) Makefile
-LIBELOGIND_CURRENT=16
-LIBELOGIND_REVISION=4
-LIBELOGIND_AGE=16
+LIBELOGIND_CURRENT=17
+LIBELOGIND_REVISION=2
+LIBELOGIND_AGE=17
# Dirs of external packages
dbuspolicydir=@dbuspolicydir@
BUILT_SOURCES =
INSTALL_EXEC_HOOKS =
UNINSTALL_EXEC_HOOKS =
-INSTALL_DATA_HOOKS =
-UNINSTALL_DATA_HOOKS =
DISTCLEAN_LOCAL_HOOKS =
CLEAN_LOCAL_HOOKS =
pkginclude_HEADERS =
check_PROGRAMS =
check_DATA =
dist_rootlibexec_DATA =
+rootlib_LTLIBRARIES =
tests=
manual_tests =
if ENABLE_TESTS
$(m4_files:.m4=)
.PHONY: $(INSTALL_EXEC_HOOKS) $(UNINSTALL_EXEC_HOOKS) \
- $(INSTALL_DATA_HOOKS) $(UNINSTALL_DATA_HOOKS) \
$(DISTCLEAN_LOCAL_HOOKS) $(CLEAN_LOCAL_HOOKS)
AM_CPPFLAGS = \
AM_LDFLAGS = $(OUR_LDFLAGS)
# ------------------------------------------------------------------------------
-define move-to-rootlibdir
- if test "$(libdir)" != "$(rootlibdir)"; then \
- $(MKDIR_P) $(DESTDIR)$(rootlibdir) && \
- so_img_name=$$(readlink $(DESTDIR)$(libdir)/$$libname) && \
- rm -f $(DESTDIR)$(libdir)/$$libname && \
- $(LN_S) --relative -f $(DESTDIR)$(rootlibdir)/$$so_img_name $(DESTDIR)$(libdir)/$$libname && \
- mv $(DESTDIR)$(libdir)/$$libname.* $(DESTDIR)$(rootlibdir); \
- fi
-endef
-
install-touch-usr-hook:
touch -c $(DESTDIR)/$(prefix)
$(SECCOMP_LIBS)
libelogind_shared_la_LDFLAGS = \
- $(AM_LDFLAGS) \
- -release $(PACKAGE_VERSION)
-
+ $(AM_LDFLAGS)
# -----------------------------------------------------------------------------
gperf_txt_sources = \
src/basic/errno-list.txt
libelogind-internal.la \
libbasic.la
-libelogind-install-hook:
- libname=libelogind.so && $(move-to-rootlibdir)
-
-libelogind-uninstall-hook:
- rm -f $(DESTDIR)$(rootlibdir)/libelogind.so*
-
-INSTALL_EXEC_HOOKS += libelogind-install-hook
-UNINSTALL_EXEC_HOOKS += libelogind-uninstall-hook
-
pkgconfiglib_DATA += \
src/libelogind/libelogind.pc
INSTALL_EXEC_HOOKS += header-install-hook
UNINSTALL_EXEC_HOOKS += header-uninstall-hook
-lib_LTLIBRARIES += \
+rootlib_LTLIBRARIES += \
libelogind.la
# ------------------------------------------------------------------------------
pamlib_LTLIBRARIES = \
pam_elogind.la
+if ENABLE_PAM_CONFIG
dist_pamconf_DATA = \
src/login/elogind-user
+endif
EXTRA_DIST += \
src/login/elogind-user.m4
install-exec-hook: $(INSTALL_EXEC_HOOKS)
-uninstall-hook: $(UNINSTALL_DATA_HOOKS) $(UNINSTALL_EXEC_HOOKS)
-
-install-data-hook: $(INSTALL_DATA_HOOKS)
+uninstall-hook: $(UNINSTALL_EXEC_HOOKS)
distclean-local: $(DISTCLEAN_LOCAL_HOOKS)
@echo $($*)
git-contrib:
- @git shortlog -s `git describe --abbrev=0`.. | cut -c8- | awk '{ print $$0 "," }' | sort -u
+ @git shortlog -s `git describe --abbrev=0`.. | cut -c8- | sed 's/ /Â /g' | awk '{ print $$0 "," }' | sort -u
EXTRA_DIST += \
tools/gdb-sd_dump_hashmaps.py
systemd System and Service Manager
+CHANGES WITH 232:
+
+ * The new RemoveIPC= option can be used to remove IPC objects owned by
+ the user or group of a service when that service exits.
+
+ * The new ProtectKernelModules= option can be used to disable explicit
+ load and unload operations of kernel modules by a service. In
+ addition access to /usr/lib/modules is removed if this option is set.
+
+ * ProtectSystem= option gained a new value "strict", which causes the
+ whole file system tree with the exception of /dev, /proc, and /sys,
+ to be remounted read-only for a service.
+
+ * The new ProtectKernelTunables= option can be used to disable
+ modification of configuration files in /sys and /proc by a service.
+ Various directories and files are remounted read-only, so access is
+ restricted even if the file permissions would allow it.
+
+ * The new ProtectControlGroups= option can be used to disable write
+ access by a service to /sys/fs/cgroup.
+
+ * Various systemd services have been hardened with
+ ProtectKernelTunables=yes, ProtectControlGroups=yes,
+ RestrictAddressFamilies=.
+
+ * Support for dynamically creating users for the lifetime of a service
+ has been added. If DynamicUser=yes is specified, user and group IDs
+ will be allocated from the range 61184..65519 for the lifetime of the
+ service. They can be resolved using the new nss-systemd.so NSS
+ module. The module must be enabled in /etc/nsswitch.conf. Services
+ started in this way have PrivateTmp= and RemoveIPC= enabled, so that
+ any resources allocated by the service will be cleaned up when the
+ service exits. They also have ProtectHome=read-only and
+ ProtectSystem=strict enabled, so they are not able to make any
+ permanent modifications to the system.
+
+ * The nss-systemd module also always resolves root and nobody, making
+ it possible to have no /etc/passwd or /etc/group files in minimal
+ container or chroot environments.
+
+ * Services may be started with their own user namespace using the new
+ boolean PrivateUsers= option. Only root, nobody, and the uid/gid
+ under which the service is running are mapped. All other users are
+ mapped to nobody.
+
+ * Support for the cgroup namespace has been added to systemd-nspawn. If
+ supported by kernel, the container system started by systemd-nspawn
+ will have its own view of the cgroup hierarchy. This new behaviour
+ can be disabled using $SYSTEMD_NSPAWN_USE_CGNS environment variable.
+
+ * The new MemorySwapMax= option can be used to limit the maximum swap
+ usage under the unified cgroup hierarchy.
+
+ * Support for the CPU controller in the unified cgroup hierarchy has
+ been added, via the CPUWeight=, CPUStartupWeight=, CPUAccounting=
+ options. This controller requires out-of-tree patches for the kernel
+ and the support is provisional.
+
+ * Mount and automount units may now be created transiently
+ (i.e. dynamically at runtime via the bus API, instead of requiring
+ unit files in the file system).
+
+ * systemd-mount is a new tool which may mount file systems – much like
+ mount(8), optionally pulling in additional dependencies through
+ transient .mount and .automount units. For example, this tool
+ automatically runs fsck on a backing block device before mounting,
+ and allows the automount logic to be used dynamically from the
+ command line for establishing mount points. This tool is particularly
+ useful when dealing with removable media, as it will ensure fsck is
+ run – if necessary – before the first access and that the file system
+ is quickly unmounted after each access by utilizing the automount
+ logic. This maximizes the chance that the file system on the
+ removable media stays in a clean state, and if it isn't in a clean
+ state is fixed automatically.
+
+ * LazyUnmount=yes option for mount units has been added to expose the
+ umount --lazy option. Similarly, ForceUnmount=yes exposes the --force
+ option.
+
+ * /efi will be used as the mount point of the EFI boot partition, if
+ the directory is present, and the mount point was not configured
+ through other means (e.g. fstab). If /efi directory does not exist,
+ /boot will be used as before. This makes it easier to automatically
+ mount the EFI partition on systems where /boot is used for something
+ else.
+
+ * When operating on GPT disk images for containers, systemd-nspawn will
+ now mount the ESP to /boot or /efi according to the same rules as PID
+ 1 running on a host. This allows tools like "bootctl" to operate
+ correctly within such containers, in order to make container images
+ bootable on physical systems.
+
+ * disk/by-id and disk/by-path symlinks are now created for NVMe drives.
+
+ * Two new user session targets have been added to support running
+ graphical sessions under the systemd --user instance:
+ graphical-session.target and graphical-session-pre.target. See
+ systemd.special(7) for a description of how those targets should be
+ used.
+
+ * The vconsole initialization code has been significantly reworked to
+ use KD_FONT_OP_GET/SET ioctls instead of KD_FONT_OP_COPY and better
+ support unicode keymaps. Font and keymap configuration will now be
+ copied to all allocated virtual consoles.
+
+ * FreeBSD's bhyve virtualization is now detected.
+
+ * Information recorded in the journal for core dumps now includes the
+ contents of /proc/mountinfo and the command line of the process at
+ the top of the process hierarchy (which is usually the init process
+ of the container).
+
+ * systemd-journal-gatewayd learned the --directory= option to serve
+ files from the specified location.
+
+ * journalctl --root=… can be used to peruse the journal in the
+ /var/log/ directories inside of a container tree. This is similar to
+ the existing --machine= option, but does not require the container to
+ be active.
+
+ * The hardware database has been extended to support
+ ID_INPUT_TRACKBALL, used in addition to ID_INPUT_MOUSE to identify
+ trackball devices.
+
+ MOUSE_WHEEL_CLICK_ANGLE_HORIZONTAL hwdb property has been added to
+ specify the click rate for mice which include a horizontal wheel with
+ a click rate that is different than the one for the vertical wheel.
+
+ * systemd-run gained a new --wait option that makes service execution
+ synchronous. (Specifically, the command will not return until the
+ specified service binary exited.)
+
+ * systemctl gained a new --wait option that causes the start command to
+ wait until the units being started have terminated again.
+
+ * A new journal output mode "short-full" has been added which displays
+ timestamps with abbreviated English day names and adds a timezone
+ suffix. Those timestamps include more information than the default
+ "short" output mode, and can be passed directly to journalctl's
+ --since= and --until= options.
+
+ * /etc/resolv.conf will be bind-mounted into containers started by
+ systemd-nspawn, if possible, so any changes to resolv.conf contents
+ are automatically propagated to the container.
+
+ * The number of instances for socket-activated services originating
+ from a single IP address can be limited with
+ MaxConnectionsPerSource=, extending the existing setting of
+ MaxConnections=.
+
+ * systemd-networkd gained support for vcan ("Virtual CAN") interface
+ configuration.
+
+ * .netdev and .network configuration can now be extended through
+ drop-ins.
+
+ * UDP Segmentation Offload, TCP Segmentation Offload, Generic
+ Segmentation Offload, Generic Receive Offload, Large Receive Offload
+ can be enabled and disabled using the new UDPSegmentationOffload=,
+ TCPSegmentationOffload=, GenericSegmentationOffload=,
+ GenericReceiveOffload=, LargeReceiveOffload= options in the
+ [Link] section of .link files.
+
+ * The Spanning Tree Protocol, Priority, Aging Time, and the Default
+ Port VLAN ID can be configured for bridge devices using the new STP=,
+ Priority=, AgeingTimeSec=, and DefaultPVID= settings in the [Bridge]
+ section of .netdev files.
+
+ * The route table to which routes received over DHCP or RA should be
+ added can be configured with the new RouteTable= option in the [DHCP]
+ and [IPv6AcceptRA] sections of .network files.
+
+ * The Address Resolution Protocol can be disabled on links managed by
+ systemd-networkd using the ARP=no setting in the [Link] section of
+ .network files.
+
+ * New environment variables $SERVICE_RESULT, $EXIT_CODE and
+ $EXIT_STATUS are set for ExecStop= and ExecStopPost= commands, and
+ encode information about the result and exit codes of the current
+ service runtime cycle.
+
+ * systemd-sysctl will now configure kernel parameters in the order
+ they occur in the configuration files. This matches what sysctl
+ has been traditionally doing.
+
+ * kernel-install "plugins" that are executed to perform various
+ tasks after a new kernel is added and before an old one is removed
+ can now return a special value to terminate the procedure and
+ prevent any later plugins from running.
+
+ * Journald's SplitMode=login setting has been deprecated. It has been
+ removed from documentation, and its use is discouraged. In a future
+ release it will be completely removed, and made equivalent to current
+ default of SplitMode=uid.
+
+ * Storage=both option setting in /etc/systemd/coredump.conf has been
+ removed. With fast LZ4 compression storing the core dump twice is not
+ useful.
+
+ * The --share-system systemd-nspawn option has been replaced with an
+ (undocumented) variable $SYSTEMD_NSPAWN_SHARE_SYSTEM, but the use of
+ this functionality is discouraged. In addition the variables
+ $SYSTEMD_NSPAWN_SHARE_NS_IPC, $SYSTEMD_NSPAWN_SHARE_NS_PID,
+ $SYSTEMD_NSPAWN_SHARE_NS_UTS may be used to control the unsharing of
+ individual namespaces.
+
+ * "machinectl list" now shows the IP address of running containers in
+ the output, as well as OS release information.
+
+ * "loginctl list" now shows the TTY of each session in the output.
+
+ * sd-bus gained new API calls sd_bus_track_set_recursive(),
+ sd_bus_track_get_recursive(), sd_bus_track_count_name(),
+ sd_bus_track_count_sender(). They permit usage of sd_bus_track peer
+ tracking objects in a "recursive" mode, where a single client can be
+ counted multiple times, if it takes multiple references.
+
+ * sd-bus gained new API calls sd_bus_set_exit_on_disconnect() and
+ sd_bus_get_exit_on_disconnect(). They may be used to to make a
+ process using sd-bus automatically exit if the bus connection is
+ severed.
+
+ * Bus clients of the service manager may now "pin" loaded units into
+ memory, by taking an explicit reference on them. This is useful to
+ ensure the client can retrieve runtime data about the service even
+ after the service completed execution. Taking such a reference is
+ available only for privileged clients and should be helpful to watch
+ running services in a race-free manner, and in particular collect
+ information about exit statuses and results.
+
+ * The nss-resolve module has been changed to strictly return UNAVAIL
+ when communication via D-Bus with resolved failed, and NOTFOUND when
+ a lookup completed but was negative. This means it is now possible to
+ neatly configure fallbacks using nsswitch.conf result checking
+ expressions. Taking benefit of this, the new recommended
+ configuration line for the "hosts" entry in /etc/nsswitch.conf is:
+
+ hosts: files mymachines resolve [!UNAVAIL=return] dns myhostname
+
+ * A new setting CtrlAltDelBurstAction= has been added to
+ /etc/systemd/system.conf which may be used to configure the precise
+ behaviour if the user on the console presses Ctrl-Alt-Del more often
+ than 7 times in 2s. Previously this would unconditionally result in
+ an expedited, immediate reboot. With this new setting the precise
+ operation may be configured in more detail, and also turned off
+ entirely.
+
+ * In .netdev files two new settings RemoteChecksumTx= and
+ RemoteChecksumRx= are now understood that permit configuring the
+ remote checksumming logic for VXLAN networks.
+
+ * The service manager learnt a new "invocation ID" concept for invoked
+ services. Each runtime cycle of a service will get a new invocation
+ ID (a 128bit random UUID) assigned that identifies the current
+ run of the service uniquely and globally. A new invocation ID
+ is generated each time a service starts up. The journal will store
+ the invocation ID of a service along with any logged messages, thus
+ making the invocation ID useful for matching the online runtime of a
+ service with the offline log data it generated in a safe way without
+ relying on synchronized timestamps. In many ways this new service
+ invocation ID concept is similar to the kernel's boot ID concept that
+ uniquely and globally identifies the runtime of each boot. The
+ invocation ID of a service is passed to the service itself via an
+ environment variable ($INVOCATION_ID). A new bus call
+ GetUnitByInvocationID() has been added that is similar to GetUnit()
+ but instead of retrieving the bus path for a unit by its name
+ retrieves it by its invocation ID. The returned path is valid only as
+ long as the passed invocation ID is current.
+
+ * systemd-resolved gained a new "DNSStubListener" setting in
+ resolved.conf. It either takes a boolean value or the special values
+ "udp" and "tcp", and configures whether to enable the stub DNS
+ listener on 127.0.0.53:53.
+
+ * IP addresses configured via networkd may now carry additional
+ configuration settings supported by the kernel. New options include:
+ HomeAddress=, DuplicateAddressDetection=, ManageTemporaryAddress=,
+ PrefixRoute=, AutoJoin=.
+
+ * The PAM configuration fragment file for "user@.service" shipped with
+ systemd (i.e. the --user instance of systemd) has been stripped to
+ the minimum necessary to make the system boot. Previously, it
+ contained Fedora-specific stanzas that did not apply to other
+ distributions. It is expected that downstream distributions add
+ additional configuration lines, matching their needs to this file,
+ using it only as rough template of what systemd itself needs. Note
+ that this reduced fragment does not even include an invocation of
+ pam_limits which most distributions probably want to add, even though
+ systemd itself does not need it. (There's also the new build time
+ option --with-pamconfdir=no to disable installation of the PAM
+ fragment entirely.)
+
+ * If PrivateDevices=yes is set for a service the CAP_SYS_RAWIO
+ capability is now also dropped from its set (in addition to
+ CAP_SYS_MKNOD as before).
+
+ * In service unit files it is now possible to connect a specific named
+ file descriptor with stdin/stdout/stdout of an executed service. The
+ name may be specified in matching .socket units using the
+ FileDescriptorName= setting.
+
+ * A number of journal settings may now be configured on the kernel
+ command line. Specifically, the following options are now understood:
+ systemd.journald.max_level_console=,
+ systemd.journald.max_level_store=,
+ systemd.journald.max_level_syslog=, systemd.journald.max_level_kmsg=,
+ systemd.journald.max_level_wall=.
+
+ * "systemctl is-enabled --full" will now show by which symlinks a unit
+ file is enabled in the unit dependency tree.
+
+ * Support for VeraCrypt encrypted partitions has been added to the
+ "cryptsetup" logic and /etc/crypttab.
+
+ * systemd-detect-virt gained support for a new --private-users switch
+ that checks whether the invoking processes are running inside a user
+ namespace. Similar, a new special value "private-users" for the
+ existing ConditionVirtualization= setting has been added, permitting
+ skipping of specific units in user namespace environments.
+
+ Contributions from: Alban Crequy, Alexander Kuleshov, Alfie John,
+ Andreas Henriksson, Andrew Jeddeloh, Balázs Úr, Bart Rulon, Benjamin
+ Richter, Ben Gamari, Ben Harris, Brian J. Murrell, Christian Brauner,
+ Christian Rebischke, Clinton Roy, Colin Walters, Cristian RodrÃguez,
+ Daniel Hahler, Daniel Mack, Daniel Maixner, Daniel Rusek, Dan Dedrick,
+ Davide Cavalca, David Herrmann, David Michael, Dennis Wassenberg,
+ Djalal Harouni, Dongsu Park, Douglas Christman, Elias Probst, Eric
+ Cook, Erik Karlsson, Evgeny Vereshchagin, Felipe Sateler, Felix Zhang,
+ Franck Bui, George Hilliard, Giuseppe Scrivano, HATAYAMA Daisuke,
+ Heikki Kemppainen, Hendrik Brueckner, hi117, Ismo Puustinen, Ivan
+ Shapovalov, Jakub Filak, Jakub Wilk, Jan Synacek, Jason Kölker,
+ Jean-Sébastien Bour, Jiřà PÃrko, Jonathan Boulle, Jorge Niedbalski,
+ Keith Busch, kristbaum, Kyle Russell, Lans Zhang, Lennart Poettering,
+ Leonardo Brondani Schenkel, Lucas Werkmeister, Luca Bruno, Lukáš
+ Nykrýn, Maciek Borzecki, Mantas Mikulėnas, Marc-Antoine Perennou,
+ Marcel Holtmann, Marcos Mello, Martin Ejdestig, Martin Pitt, Matej
+ Habrnal, Maxime de Roucy, Michael Biebl, Michael Chapman, Michael Hoy,
+ Michael Olbrich, Michael Pope, Michal Sekletar, Michal Soltys, Mike
+ Gilbert, Nick Owens, Patrik Flykt, Paweł Szewczyk, Peter Hutterer,
+ Piotr DrÄ…g, Reid Price, Richard W.M. Jones, Roman Stingler, Ronny
+ Chevalier, Seraphime Kirkovski, Stefan Schweter, Steve Muir, Susant
+ Sahani, Tejun Heo, Thomas Blume, Thomas H. P. Andersen, Tiago Levit,
+ Tobias Jungel, Tomáš Janoušek, Topi Miettinen, Torstein Husebø, Umut
+ Tezduyar Lindskog, Vito Caputo, WaLyong Cho, Wilhelm Schuster, Yann
+ E. MORIN, Yi EungJun, Yuki Inoguchi, Yu Watanabe, Zbigniew
+ Jędrzejewski-Szmek, Zeal Jagannatha
+
+ — Santa Fe, 2016-11-03
+
CHANGES WITH 231:
* In service units the various ExecXYZ= settings have been extended
file. It can be used in lieu of %systemd_requires in packages which
don't use any systemd functionality and are intended to be installed
in minimal containers without systemd present. This macro provides
- ordering dependecies to ensure that if the package is installed in
+ ordering dependencies to ensure that if the package is installed in
the same rpm transaction as systemd, systemd will be installed before
the scriptlets for the package are executed, allowing unit presets
to be handled.
"mkosi" is invoked in the build tree a new raw OS image is generated
incorporating the systemd sources currently being worked on and a
clean, fresh distribution installation. The generated OS image may be
- booted up with "systemd-nspawn -b -i", qemu-kvm or on any physcial
+ booted up with "systemd-nspawn -b -i", qemu-kvm or on any physical
UEFI PC. This functionality is particularly useful to easily test
local changes made to systemd in a pristine, defined environment. See
HACKING for details.
+ * configure learned the --with-support-url= option to specify the
+ distribution's bugtracker.
+
Contributions from: Alban Crequy, Alessandro Puccetti, Alessio Igor
Bogani, Alexander Kuleshov, Alexander Kurtz, Alex Gaynor, Andika
Triwidada, Andreas Pokorny, Andreas Rammhold, Andrew Jeddeloh, Ansgar
of the owners and the ACLs of all files and directories in a
container tree to match the UID/GID user namespacing range selected
for the container invocation. This mode is enabled via the new
- --private-user-chown switch. It also gained support for automatically
- choosing a free, previously unused UID/GID range when starting a
- container, via the new --private-users=pick setting (which implies
- --private-user-chown). Together, these options for the first time
- make user namespacing for nspawn containers fully automatic and thus
- deployable. The systemd-nspawn@.service template unit file has been
- changed to use this functionality by default.
+ --private-users-chown switch. It also gained support for
+ automatically choosing a free, previously unused UID/GID range when
+ starting a container, via the new --private-users=pick setting (which
+ implies --private-users-chown). Together, these options for the first
+ time make user namespacing for nspawn containers fully automatic and
+ thus deployable. The systemd-nspawn@.service template unit file has
+ been changed to use this functionality by default.
* systemd-nspawn gained a new --network-zone= switch, that allows
creating ad-hoc virtual Ethernet links between multiple containers,
--user instance of systemd these specifiers where correctly
resolved, but hardly made any sense, since the user instance
lacks privileges to do user switches anyway, and User= is
- hence useless. Morever, even in the --user instance of
+ hence useless. Moreover, even in the --user instance of
systemd behaviour was awkward as it would only take settings
from User= assignment placed before the specifier into
account. In order to unify and simplify the logic around
* The RuntimeDirectory= setting now understands unit
specifiers like %i or %f.
- * A new (still internal) libary API sd-ipv4acd has been added,
+ * A new (still internal) library API sd-ipv4acd has been added,
that implements address conflict detection for IPv4. It's
based on code from sd-ipv4ll, and will be useful for
detecting DHCP address conflicts.
* systemd-nspawn gained a new --property= setting to set unit
properties for the container scope. This is useful for
- setting resource parameters (e.g "CPUShares=500") on
+ setting resource parameters (e.g. "CPUShares=500") on
containers started from the command line.
* systemd-nspawn gained a new --private-users= switch to make
files.
* systemd now provides a way to store file descriptors
- per-service in PID 1.This is useful for daemons to ensure
+ per-service in PID 1. This is useful for daemons to ensure
that fds they require are not lost during a daemon
restart. The fds are passed to the daemon on the next
invocation in the same way socket activation fds are
time, the extended attribute calls have moved to glibc, and
libattr is thus unnecessary.
- * Virtualization detection works without priviliges now. This
+ * Virtualization detection works without privileges now. This
means the systemd-detect-virt binary no longer requires
CAP_SYS_PTRACE file capabilities, and our daemons can run
with fewer privileges.
/usr/lib/net/links/99-default.link. Old
80-net-name-slot.rules udev configuration file has been
removed, so local configuration overriding this file should
- be adapated to override 99-default.link instead.
+ be adapted to override 99-default.link instead.
* When the User= switch is used in a unit file, also
initialize $SHELL= based on the user database entry.
* logind's device ACLs may now be applied to these "dead"
devices nodes too, thus finally allowing managed access to
- devices such as /dev/snd/sequencer whithout loading the
+ devices such as /dev/snd/sequencer without loading the
backing module right-away.
* A new RPM macro has been added that may be used to apply
processes executed in parallel based on the number of available
CPUs instead of the amount of available RAM. This is supposed
to provide a more reliable default and limit a too aggressive
- paralellism for setups with 1000s of devices connected.
+ parallelism for setups with 1000s of devices connected.
Contributions from: Auke Kok, Colin Walters, Cristian
RodrÃguez, Daniel Buch, Dave Reisner, Frederic Crozat, Hannes
presenting log data.
* systemctl will no longer show control group information for
- a unit if a the control group is empty anyway.
+ a unit if the control group is empty anyway.
* logind can now automatically suspend/hibernate/shutdown the
system on idle.
* The SysV search path is no longer exported on the D-Bus
Manager object.
- * The Names= option is been removed from unit file parsing.
+ * The Names= option has been removed from unit file parsing.
* There's a new man page bootup(7) detailing the boot process.
about this in more detail.
* var-run.mount and var-lock.mount are no longer provided
- (which prevously bind mounted these directories to their new
+ (which previously bind mounted these directories to their new
places). Distributions which have not converted these
directories to symlinks should consider stealing these files
from git history and add them downstream.
* Many bugfixes for the journal, including endianness fixes and
ensuring that disk space enforcement works
- * sd-login.h is C++ comptaible again
+ * sd-login.h is C++ compatible again
* Extend the /etc/os-release format on request of the Debian
folks
* New man pages for all APIs from libsystemd-login.
- * The build tree got reorganized and a the build system is a
+ * The build tree got reorganized and the build system is a
lot more modular allowing embedded setups to specifically
select the components of systemd they are interested in.
Janitorial Clean-ups:
-* code cleanup: retire FOREACH_WORD_QUOTED, port to extract_first_word() loops instead
+* code cleanup: retire FOREACH_WORD_QUOTED, port to extract_first_word() loops instead.
+ For example, most conf parsing callbacks should use it.
* replace manual readdir() loops with FOREACH_DIRENT or FOREACH_DIRENT_ALL
-* Get rid of the last strerror() invocations in favour of %m and strerror_r()
-
* Rearrange tests so that the various test-xyz.c match a specific src/basic/xyz.c again
Features:
+* drop nss-myhostname in favour of nss-resolve?
+
+* drop internal dlopen() based nss-dns fallback in nss-resolve, and rely on the
+ external nsswitch.conf based one
+
+* add a percentage syntax for TimeoutStopSec=, e.g. TimeoutStopSec=150%, and
+ then use that for the setting used in user@.service. It should be understood
+ relative to the configured default value.
+
+* on cgroupsv2 add DelegateControllers=, to pick the precise cgroup controllers to delegate
+
+* in networkd, when matching device types, fix up DEVTYPE rubbish the kernel passes to us
+
+* enable LockMLOCK to take a percentage value relative to physical memory
+
+* switch to ProtectSystem=strict for all our long-running services where that's possible
+
+* If RootDirectory= is used, mount /proc, /sys, /dev into it, if not mounted yet
+
+* Permit masking specific netlink APIs with RestrictAddressFamily=
+
+* nspawn: start UID allocation loop from hash of container name
+
+* nspawn: support that /proc, /sys/, /dev are pre-mounted
+
+* define gpt header bits to select volatility mode
+
+* nspawn: mount loopback filesystems with "discard"
+
* ProtectKernelLogs= (drops CAP_SYSLOG, add seccomp for syslog() syscall, and DeviceAllow to /dev/kmsg) in service files
* ProtectClock= (drops CAP_SYS_TIMES, adds seecomp filters for settimeofday, adjtimex), sets DeviceAllow o /dev/rtc
-* ProtectMount= (drop mount/umount/pivot_root from seccomp, disallow fuse via DeviceAllow, imply Mountflags=slave)
+* ProtectKernelModules= (drops CAP_SYS_MODULE and filters the kmod syscalls)
+
+* ProtectTracing= (drops CAP_SYS_PTRACE, blocks ptrace syscall, makes /sys/kernel/tracing go away)
-* ProtectDevices= should also take iopl/ioperm/pciaccess away
+* ProtectMount= (drop mount/umount/pivot_root from seccomp, disallow fuse via DeviceAllow, imply Mountflags=slave)
* ProtectKeyRing= to take keyring calls away
-* PrivateUsers= which maps the all user ids except root and the one specified
- in User= to nobody
+* RemoveKeyRing= to remove all keyring entries of the specified user
+
+* ProtectReboot= that masks reboot() and kexec_load() syscalls, prohibits kill
+ on PID 1 with the relevant signals, and makes relevant files in /sys and
+ /proc (such as the sysrq stuff) unavailable
-* Add AllocateUser= for allowing dynamic user ids per-service
+* DeviceAllow= should also generate seccomp filters for mknod()
* Add DataDirectory=, CacheDirectory= and LogDirectory= to match
RuntimeDirectory=, and create it as necessary when starting a service, owned by the right user.
* Add BindDirectory= for allowing arbitrary, private bind mounts for services
-* Beef up RootDirectory= to use namespacing/bind mounts as soon as fs
- namespaces are enabled by the service
-
* Add RootImage= for mounting a disk image or file as root directory
* RestrictNamespaces= or so in services (taking away the ability to create namespaces, with setns, unshare, clone)
-* nspawn: make /proc/sys/net writable?
-
* make sure the ratelimit object can deal with USEC_INFINITY as way to turn off things
* journalctl: make sure -f ends when the container indicated by -M terminates
+* mount: automatically search for "main" partition of an image has multiple
+ partitions
+
* expose the "privileged" flag of ExecCommand on the bus, and open it up to
transient units
+* in nss-systemd, if we run inside of RootDirectory= with PrivateUsers= set,
+ find a way to map the User=/Group= of the service to the right name. This way
+ a user/group for a service only has to exist on the host for the right
+ mapping to work.
+
* allow attaching additional journald log fields to cgroups
+* add bus API for creating unit files in /etc, reusing the code for transient units
+
+* add bus API to remove unit files from /etc
+
+* add bus API to retrieve current unit file contents (i.e. implement "systemctl cat" on the bus only)
+
* rework fopen_temporary() to make use of open_tmpfile_linkable() (problem: the
kernel doesn't support linkat() that replaces existing files, currently)
* journald: sigbus API via a signal-handler safe function that people may call
from the SIGBUS handler
-* when using UTF8, ellipsize with "…" rather than "...", so that we can show more contents before truncating
-
* move specifier expansion from service_spawn() into load-fragment.c
* optionally, also require WATCHDOG=1 notifications during service start-up and shutdown
* add systemctl stop --job-mode=triggering that follows TRIGGERED_BY deps and adds them to the same transaction
-* Maybe add a way how users can "pin" units into memory, so that they are not subject to automatic GC?
-
* PID1: find a way how we can reload unit file configuration for
specific units only, without reloading the whole of systemd
* PID 1 should send out sd_notify("WATCHDOG=1") messages (for usage in the --user mode, and when run via nspawn)
-* consider throwing a warning if a service declares it wants to be "Before=" a .device unit.
-
* there's probably something wrong with having user mounts below /sys,
as we have for debugfs. for exmaple, src/core/mount.c handles mounts
prefixed with /sys generally special.
* implement a per-service firewall based on net_cls
* Port various tools to make use of verbs.[ch], where applicable: busctl,
- bootctl, coredumpctl, hostnamectl, localectl, systemd-analyze, timedatectl
+ coredumpctl, hostnamectl, localectl, systemd-analyze, timedatectl
* hostnamectl: show root image uuid
* synchronize console access with BSD locks:
http://lists.freedesktop.org/archives/systemd-devel/2014-October/024582.html
-* as soon as we have kdbus, and sender timestamps, revisit coalescing multiple parallel daemon reloads:
+* as soon as we have sender timestamps, revisit coalescing multiple parallel daemon reloads:
http://lists.freedesktop.org/archives/systemd-devel/2014-December/025862.html
* in systemctl list-unit-files: show the install value the presets would suggest for a service in a third column
* timesyncd: add ugly bus calls to set NTP servers per-interface, for usage by NM
-* extract_many_words() should probably be used by a lot of code that
- currently uses FOREACH_WORD and friends. For example, most conf
- parsing callbacks should use it.
-
* merge ~/.local/share and ~/.local/lib into one similar /usr/lib and /usr/share....
* systemd.show_status= should probably have a mode where only failed
* MessageQueueMessageSize= (and suchlike) should use parse_iec_size().
-* "busctl status" works only as root on dbus1, since we cannot read
- /proc/$PID/exe
-
* implement Distribute= in socket units to allow running multiple
service instances processing the listening socket, and open this up
for ReusePort=
and passes this back to PID1 via SCM_RIGHTS. This also could be used
to allow Chown/chgrp on sockets without requiring NSS in PID 1.
-* New service property: maximum CPU runtime for a service
-
* introduce bus call FreezeUnit(s, b), as well as "systemctl freeze
$UNIT" and "systemctl thaw $UNIT" as wrappers around this. The calls
should SIGSTOP all unit processes in a loop until all processes of
error. Currently, we just ignore it and read the unit from the search
path anyway.
-* refuse boot if /etc/os-release is missing or /etc/machine-id cannot be set up
+* refuse boot if /usr/lib/os-release is missing or /etc/machine-id cannot be set up
* btrfs raid assembly: some .device jobs stay stuck in the queue
-* make sure gdm does not use multi-user-x but the new default X configuration file, and then remove multi-user-x from systemd
-
* man: the documentation of Restart= currently is very misleading and suggests the tools from ExecStartPre= might get restarted.
* load .d/*.conf dropins for device units
message that works, but alraedy after a short tiemout
- check if we can make journalctl by default use --follow mode inside of less if called without args?
- maybe add API to send pairs of iovecs via sd_journal_send
- - journal: when writing journal auto-rotate if time jumps backwards
- journal: add a setgid "systemd-journal" utility to invoke from libsystemd-journal, which passes fds via STDOUT and does PK access
- journactl: support negative filtering, i.e. FOOBAR!="waldo",
and !FOOBAR for events without FOOBAR.
* currently x-systemd.timeout is lost in the initrd, since crypttab is copied into dracut, but fstab is not
* nspawn:
- - to allow "linking" of nspawn containers, extend --network-bridge= so
- that it can dynamically create bridge interfaces that are refcounted
- by the containers on them. For each group of containers to link together
- nspawn -x should support ephemeral instances of gpt images
- emulate /dev/kmsg using CUSE and turn off the syslog syscall
with seccomp. That should provide us with a useful log buffer that
- as soon as networkd has a bus interface, hook up --network-interface=,
--network-bridge= with networkd, to trigger netdev creation should an
interface be missing
- - don't copy /etc/resolv.conf from host into container unless we are in
- shared-network mode
- a nice way to boot up without machine id set, so that it is set at boot
automatically for supporting --ephemeral. Maybe hash the host machine id
together with the machine name to generate the machine id for the container
is used
* machined:
- - "machinectl list" should probably show columns for OS version and IP
- addresses
- add an API so that libvirt-lxc can inform us about network interfaces being
removed or added to an existing machine
- "machinectl migrate" or similar to copy a container from or to a
* coredump:
- save coredump in Windows/Mozilla minidump format
- - move PID 1 segfaults to /var/lib/systemd/coredump?
+ - when truncating coredumps, also log the full size that the process had, and make a metadata field so we can report truncated coredumps
* support crash reporting operation modes (https://live.gnome.org/GnomeOS/Design/Whiteboards/ProblemReporting)
- GC unreferenced jobs (such as .device jobs)
- move PAM code into its own binary
- when we automatically restart a service, ensure we restart its rdeps, too.
- - for services: do not set $HOME in services unless requested
- hide PAM options in fragment parser when compile time disabled
- Support --test based on current system state
- If we show an error about a unit (such as not showing up) and it has no Description string, then show a description string generated form the reverse of unit_name_mangle().
- add reduced [Link] support to .network files
- add Scope= parsing option for [Network]
- properly handle routerless dhcp leases
- - add more attribute support for SIT tunnel
- work with non-Ethernet devices
- add support for more bond options
- dhcp: do we allow configuring dhcp routes on interfaces that are not the one we got the dhcp info from?
support Name=foo*|bar*|baz ?
- duplicate address check for static IPs (like ARPCHECK in network-scripts)
- allow DUID/IAID to be customized, see issue #394.
- - support configuration option for TSO (tcp segmentation offload)
- whenever uplink info changes, make DHCP server send out FORCERENEW
* networkd-wait-online:
or interface down
- some servers don't do rapid commit without a filled in IA_NA, verify
this behavior
+ - RouteTable= ?
External:
<Unit filename="../src/libelogind/sd-event/sd-event.c">
<Option compilerVar="CC" />
</Unit>
+ <Unit filename="../src/libelogind/sd-id128/id128-util.c">
+ <Option compilerVar="CC" />
+ </Unit>
+ <Unit filename="../src/libelogind/sd-id128/id128-util.h" />
<Unit filename="../src/libelogind/sd-id128/sd-id128.c">
<Option compilerVar="CC" />
</Unit>
--- /dev/null
+@@
+expression p, q;
+@@
+- free(p);
+- p = q;
+- q = NULL;
+- return 0;
++ return free_and_replace(p, q);
+@@
+expression p, q;
+@@
+- free(p);
+- p = q;
+- q = NULL;
++ free_and_replace(p, q);
AC_PREREQ([2.64])
AC_INIT([elogind],
- [231.4],
+ [232.2],
[https://github.com/elogind/elogind/issues],
[elogind],
[https://github.com/elogind/elogind])
-Wno-gnu-variable-sized-type-not-at-end \
])])
-
-# ------------------------------------------------------------------------------
-# On some distributions -flto, even if available, leads to linking errors.
-# We therefore make it optional
-have_lto=no
-AC_ARG_ENABLE([lto],
- [AS_HELP_STRING([--disable-lto], [Disable link time optimization (default: test)])])
-
-AS_IF([test "x$enable_lto" != "xno"], [
- AS_CASE([$CFLAGS], [*-O[[12345sz\ ]]*],
- [have_lto=yes],
- [AC_MSG_RESULT([skipping -flto, optimization not enabled])])
- AS_IF([test "$have_lto" != "no"],
- [CC_CHECK_FLAGS_APPEND([with_cflags], [CFLAGS], [-flto])]
- )
- AC_SUBST([OUR_CFLAGS], "$with_cflags $sanitizer_cflags")
-])
+AC_ARG_ENABLE([lto], [AS_HELP_STRING([--disable-lto], [disable -flto])],
+ [], [enable_lto=yes])
+AS_CASE([$CFLAGS], [*-O[[12345sz\ ]]*],
+ [AS_IF([test "x$enable_lto" = "xyes"],
+ [CC_CHECK_FLAGS_APPEND([with_cflags], [CFLAGS], [-flto])],
+ [AC_MSG_RESULT([disabling -flto as requested])])],
+ [AC_MSG_RESULT([skipping -flto, optimization not enabled])])
+AC_SUBST([OUR_CFLAGS], "$with_cflags $sanitizer_cflags")
# ------------------------------------------------------------------------------
AS_CASE([$CFLAGS], [*-O[[12345sz\ ]]*],
AX_NORMALIZE_PATH([with_pamlibdir])
AC_ARG_WITH([pamconfdir],
- AS_HELP_STRING([--with-pamconfdir=DIR], [Directory for PAM configuration]),
+ AS_HELP_STRING([--with-pamconfdir=DIR], [Directory for PAM configuration (pass no to disable installing)]),
[],
[with_pamconfdir=${sysconfdir}/pam.d])
+AM_CONDITIONAL(ENABLE_PAM_CONFIG, [test "$with_pamconfdir" != "no"])
AX_NORMALIZE_PATH([with_pamconfdir])
AC_ARG_ENABLE([split-usr],
test coverage: ${have_coverage}
Split /usr: ${enable_split_usr}
utmp/wtmp support: ${have_utmp}
- Link time optimization: ${have_lto}
extra debugging: ${enable_debug}
cgroup controller: ${with_cgroupctrl}
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<refsect1>
- <title>Environment</title>
-
- <variablelist class='environment-variables'>
- <varlistentry id='pager'>
- <term><varname>$SYSTEMD_PAGER</varname></term>
-
- <listitem><para>Pager to use when
- <option>--no-pager</option> is not given;
- overrides <varname>$PAGER</varname>. Setting
- this to an empty string or the value
- <literal>cat</literal> is equivalent to passing
- <option>--no-pager</option>.</para></listitem>
- </varlistentry>
-
- <varlistentry id='less'>
- <term><varname>$SYSTEMD_LESS</varname></term>
-
- <listitem><para>Override the default
- options passed to
- <command>less</command>
- (<literal>FRSXMK</literal>).</para></listitem>
- </varlistentry>
- </variablelist>
+ <title>Environment</title>
+
+ <variablelist class='environment-variables'>
+ <varlistentry id='pager'>
+ <term><varname>$SYSTEMD_PAGER</varname></term>
+
+ <listitem><para>Pager to use when <option>--no-pager</option> is not given; overrides
+ <varname>$PAGER</varname>. If neither <varname>$SYSTEMD_PAGER</varname> nor <varname>$PAGER</varname> are set, a
+ set of well-known pager implementations are tried in turn, including
+ <citerefentry><refentrytitle>less</refentrytitle><manvolnum>1</manvolnum></citerefentry> and
+ <citerefentry><refentrytitle>more</refentrytitle><manvolnum>1</manvolnum></citerefentry>, until one is found. If
+ no pager implementation is discovered no pager is invoked. Setting this environment variable to an empty string
+ or the value <literal>cat</literal> is equivalent to passing <option>--no-pager</option>.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id='less'>
+ <term><varname>$SYSTEMD_LESS</varname></term>
+
+ <listitem><para>Override the options passed to <command>less</command> (by default
+ <literal>FRSXMK</literal>).</para></listitem>
+ </varlistentry>
+
+ <varlistentry id='lesscharset'>
+ <term><varname>$SYSTEMD_LESSCHARSET</varname></term>
+
+ <listitem><para>Override the charset passed to <command>less</command> (by default <literal>utf-8</literal>, if
+ the invoking terminal is determined to be UTF-8 compatible).</para></listitem>
+ </varlistentry>
+
+ </variablelist>
</refsect1>
iteration a single event source is dispatched. Each time an event
source is dispatched the kernel is polled for new events, before
the next event source is dispatched. The event loop is designed to
- honour priorities and provide fairness within each priority. It is
+ honor priorities and provide fairness within each priority. It is
not designed to provide optimal throughput, as this contradicts
these goals due the limitations of the underlying <citerefentry
project='man-pages'><refentrytitle>epoll</refentrytitle><manvolnum>7</manvolnum></citerefentry>
<funcsynopsisinfo>#include <elogind/sd-event.h></funcsynopsisinfo>
<funcsynopsisinfo><token>enum</token> {
- <constant>SD_EVENT_SOURCE_IMPORTANT</constant> = -100,
- <constant>SD_EVENT_SOURCE_NORMAL</constant> = 0,
- <constant>SD_EVENT_SOURCE_IDLE</constant> = 100,
+ <constant>SD_EVENT_PRIORITY_IMPORTANT</constant> = -100,
+ <constant>SD_EVENT_PRIORITY_NORMAL</constant> = 0,
+ <constant>SD_EVENT_PRIORITY_IDLE</constant> = 100,
};</funcsynopsisinfo>
<funcprototype>
reliable. However, it is guaranteed that if events are seen on
multiple same-priority event sources at the same time, each one is
not dispatched again until all others have been dispatched
- once. This behaviour guarantees that within each priority
+ once. This behavior guarantees that within each priority
particular event sources do not starve or dominate the event
loop.</para>
<refnamediv>
<refname>sd_id128_get_machine</refname>
<refname>sd_id128_get_boot</refname>
+ <refname>sd_id128_get_invocation</refname>
<refpurpose>Retrieve 128-bit IDs</refpurpose>
</refnamediv>
<paramdef>sd_id128_t *<parameter>ret</parameter></paramdef>
</funcprototype>
+ <funcprototype>
+ <funcdef>int <function>sd_id128_get_invocation</function></funcdef>
+ <paramdef>sd_id128_t *<parameter>ret</parameter></paramdef>
+ </funcprototype>
+
</funcsynopsis>
</refsynopsisdiv>
for more information. This function also internally caches the
returned ID to make this call a cheap operation.</para>
- <para>Note that <function>sd_id128_get_boot()</function> always
- returns a UUID v4 compatible ID.
- <function>sd_id128_get_machine()</function> will also return a
- UUID v4-compatible ID on new installations but might not on older.
- It is possible to convert the machine ID into a UUID v4-compatible
+ <para><function>sd_id128_get_invocation()</function> returns the invocation ID of the currently executed
+ service. In its current implementation, this reads and parses the <varname>$INVOCATION_ID</varname> environment
+ variable that the service manager sets when activating a service, see
+ <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry> for details. The
+ ID is cached internally. In future a different mechanism to determine the invocation ID may be added.</para>
+
+ <para>Note that <function>sd_id128_get_boot()</function> and <function>sd_id128_get_invocation()</function> always
+ return UUID v4 compatible IDs. <function>sd_id128_get_machine()</function> will also return a UUID v4-compatible
+ ID on new installations but might not on older. It is possible to convert the machine ID into a UUID v4-compatible
one. For more information, see
<citerefentry><refentrytitle>machine-id</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
<refsect1>
<title>Notes</title>
- <para>The <function>sd_id128_get_machine()</function> and
- <function>sd_id128_get_boot()</function> interfaces are available
- as a shared library, which can be compiled and linked to with the
- <literal>libelogind</literal>Â <citerefentry project='die-net'><refentrytitle>pkg-config</refentrytitle><manvolnum>1</manvolnum></citerefentry>
- file.</para>
+ <para>The <function>sd_id128_get_machine()</function>, <function>sd_id128_get_boot()</function> and
+ <function>sd_id128_get_invocation()</function> interfaces are available as a shared library, which can be compiled
+ and linked to with the <literal>libsystemd</literal>Â <citerefentry
+ project='die-net'><refentrytitle>pkg-config</refentrytitle><manvolnum>1</manvolnum></citerefentry> file.</para>
</refsect1>
<refsect1>
<title>See Also</title>
<para>
- <citerefentry><refentrytitle>elogind</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd-id128</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>machine-id</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>random</refentrytitle><manvolnum>4</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>sd_id128_randomize</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+ <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_id128_randomize</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry project='man-pages'><refentrytitle>random</refentrytitle><manvolnum>4</manvolnum></citerefentry>
</para>
</refsect1>
<varlistentry>
<term>FDSTORE=1</term>
- <listitem><para>Stores additional file descriptors in the
- service manager. File descriptors sent this way will be
- maintained per-service by the service manager and be passed
- again using the usual file descriptor passing logic on the
- next invocation of the service (see
- <citerefentry><refentrytitle>sd_listen_fds</refentrytitle><manvolnum>3</manvolnum></citerefentry>).
- This is useful for implementing service restart schemes where
- services serialize their state to <filename>/run</filename>,
- push their file descriptors to the system manager, and are
- then restarted, retrieving their state again via socket
- passing and <filename>/run</filename>. Note that the service
- manager will accept messages for a service only if
- <varname>FileDescriptorStoreMax=</varname> is set to non-zero
- for it (defaults to zero). See
- <citerefentry><refentrytitle>systemd.service</refentrytitle><manvolnum>5</manvolnum></citerefentry>
- for details. Multiple arrays of file descriptors may be sent
- in separate messages, in which case the arrays are combined.
- Note that the service manager removes duplicate file
- descriptors before passing them to the service. Use
- <function>sd_pid_notify_with_fds()</function> to send messages
- with <literal>FDSTORE=1</literal>, see
- below.</para></listitem>
+ <listitem><para>Stores additional file descriptors in the service manager. File
+ descriptors sent this way will be maintained per-service by the service manager
+ and will be passed again using the usual file descriptor passing logic on the next
+ invocation of the service, see
+ <citerefentry><refentrytitle>sd_listen_fds</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
+ This is useful for implementing service restart schemes where services serialize
+ their state to <filename>/run</filename>, push their file descriptors to the
+ system manager, and are then restarted, retrieving their state again via socket
+ passing and <filename>/run</filename>. Note that the service manager will accept
+ messages for a service only if <varname>FileDescriptorStoreMax=</varname> is set
+ to non-zero for it (defaults to zero, see
+ <citerefentry><refentrytitle>systemd.service</refentrytitle><manvolnum>5</manvolnum></citerefentry>).
+ File descriptors must be pollable, see
+ <citerefentry><refentrytitle>epoll_ctl</refentrytitle><manvolnum>2</manvolnum></citerefentry>.
+ Multiple arrays of file descriptors may be sent in separate messages, in which
+ case the arrays are combined. Note that the service manager removes duplicate
+ file descriptors before passing them to the service. Use
+ <function>sd_pid_notify_with_fds()</function> to send messages with
+ <literal>FDSTORE=1</literal>, see below.</para></listitem>
</varlistentry>
<varlistentry>
</listitem>
</varlistentry>
+ <varlistentry id='no-ask-password'>
+ <term><option>--no-ask-password</option></term>
+
+ <listitem><para>Do not query the user for authentication for privileged operations.</para></listitem>
+ </varlistentry>
+
<varlistentry id='no-legend'>
<term><option>--no-legend</option></term>
return NULL;
}
+#define free_and_replace(a, b) \
+ ({ \
+ free(a); \
+ (a) = (b); \
+ (b) = NULL; \
+ 0; \
+ })
+
void* memdup(const void *p, size_t l) _alloc_(2);
static inline void freep(void *p) {
int fd;
fd = socket(AF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, NETLINK_AUDIT);
- if (fd < 0)
- cached_use = errno != EAFNOSUPPORT && errno != EPROTONOSUPPORT;
+ if (fd < 0) {
+ cached_use = !IN_SET(errno, EAFNOSUPPORT, EPROTONOSUPPORT, EPERM);
+ if (errno == EPERM)
+ log_debug_errno(errno, "Audit access prohibited, won't talk to audit");
+ }
else {
cached_use = true;
safe_close(fd);
if (setresgid(gid, gid, gid) < 0)
return log_error_errno(errno, "Failed to change group ID: %m");
- if (setgroups(0, NULL) < 0)
- return log_error_errno(errno, "Failed to drop auxiliary groups list: %m");
+ r = maybe_setgroups(0, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to drop auxiliary groups list: %m");
/* Ensure we keep the permitted caps across the setresuid() */
if (prctl(PR_SET_KEEPCAPS, 1) < 0)
return -ENOENT;
}
+#if 0 /// UNNEEDED by elogind
+bool cg_ns_supported(void) {
+ static thread_local int enabled = -1;
+
+ if (enabled >= 0)
+ return enabled;
+
+ if (access("/proc/self/ns/cgroup", F_OK) == 0)
+ enabled = 1;
+ else
+ enabled = 0;
+
+ return enabled;
+}
+#endif //0
+
int cg_enumerate_subgroups(const char *controller, const char *path, DIR **_d) {
_cleanup_free_ char *fs = NULL;
int r;
if (!cg_controller_is_valid(controller))
return -EINVAL;
- unified = cg_unified();
+ unified = cg_all_unified();
if (unified < 0)
return unified;
if (!cg_controller_is_valid(controller))
return -EINVAL;
- unified = cg_unified();
+ unified = cg_all_unified();
if (unified < 0)
return unified;
if (unified > 0) {
if (r < 0)
return r;
- unified = cg_unified();
+ unified = cg_unified(controller);
if (unified < 0)
return unified;
if (unified)
return 0;
}
+
+int cg_set_xattr(const char *controller, const char *path, const char *name, const void *value, size_t size, int flags) {
+ _cleanup_free_ char *fs = NULL;
+ int r;
+
+ assert(path);
+ assert(name);
+ assert(value || size <= 0);
+
+ r = cg_get_path(controller, path, NULL, &fs);
+ if (r < 0)
+ return r;
+
+ if (setxattr(fs, name, value, size, flags) < 0)
+ return -errno;
+
+ return 0;
+}
+
+int cg_get_xattr(const char *controller, const char *path, const char *name, void *value, size_t size) {
+ _cleanup_free_ char *fs = NULL;
+ ssize_t n;
+ int r;
+
+ assert(path);
+ assert(name);
+
+ r = cg_get_path(controller, path, NULL, &fs);
+ if (r < 0)
+ return r;
+
+ n = getxattr(fs, name, value, size);
+ if (n < 0)
+ return -errno;
+
+ return (int) n;
+}
#endif // 0
int cg_pid_get_path(const char *controller, pid_t pid, char **path) {
assert(path);
assert(pid >= 0);
- unified = cg_unified();
+ if (controller) {
+ if (!cg_controller_is_valid(controller))
+ return -EINVAL;
+ } else
+ controller = SYSTEMD_CGROUP_CONTROLLER;
+
+ unified = cg_unified(controller);
if (unified < 0)
return unified;
- if (unified == 0) {
- if (controller) {
- if (!cg_controller_is_valid(controller))
- return -EINVAL;
- } else
- controller = SYSTEMD_CGROUP_CONTROLLER;
-
+ if (unified == 0)
cs = strlen(controller);
- }
fs = procfs_file_alloca(pid, "cgroup");
log_debug_elogind("Searching for PID %u in \"%s\" (controller \"%s\")",
assert(agent);
- unified = cg_unified();
+ unified = cg_unified(controller);
if (unified < 0)
return unified;
if (unified) /* doesn't apply to unified hierarchy */
_cleanup_free_ char *fs = NULL;
int r, unified;
- unified = cg_unified();
+ unified = cg_unified(controller);
if (unified < 0)
return unified;
if (unified) /* Doesn't apply to unified hierarchy */
if (controller && (isempty(path) || path_equal(path, "/")))
return false;
- unified = cg_unified();
+ unified = cg_unified(controller);
if (unified < 0)
return unified;
}
p = path_startswith(cgroup, root);
+#if 0 /// With other controllers, elogind might end up in /elogind, and *p is 0
+ if (p && p > cgroup)
+#else
if (p && p[0] && (p > cgroup))
+#endif // 0
*shifted = p - 1;
else
*shifted = cgroup;
if (!e) {
char *s;
- s = strdup("-.slice");
+ s = strdup(SPECIAL_ROOT_SLICE);
if (!s)
return -ENOMEM;
assert(unit);
assert(ret);
- if (streq(unit, "-.slice")) {
+ if (streq(unit, SPECIAL_ROOT_SLICE)) {
char *x;
x = strdup("");
}
#if 0 /// UNNEEDED by elogind
+int cg_get_keyed_attribute(const char *controller, const char *path, const char *attribute, const char **keys, char **values) {
+ _cleanup_free_ char *filename = NULL, *content = NULL;
+ char *line, *p;
+ int i, r;
+
+ for (i = 0; keys[i]; i++)
+ values[i] = NULL;
+
+ r = cg_get_path(controller, path, attribute, &filename);
+ if (r < 0)
+ return r;
+
+ r = read_full_file(filename, &content, NULL);
+ if (r < 0)
+ return r;
+
+ p = content;
+ while ((line = strsep(&p, "\n"))) {
+ char *key;
+
+ key = strsep(&line, " ");
+
+ for (i = 0; keys[i]; i++) {
+ if (streq(key, keys[i])) {
+ values[i] = strdup(line);
+ break;
+ }
+ }
+ }
+
+ for (i = 0; keys[i]; i++) {
+ if (!values[i]) {
+ for (i = 0; keys[i]; i++) {
+ free(values[i]);
+ values[i] = NULL;
+ }
+ return -ENOENT;
+ }
+ }
+
+ return 0;
+}
+
int cg_create_everywhere(CGroupMask supported, CGroupMask mask, const char *path) {
CGroupController c;
int r, unified;
return r;
/* If we are in the unified hierarchy, we are done now */
- unified = cg_unified();
+ unified = cg_all_unified();
if (unified < 0)
return unified;
if (unified > 0)
if (r < 0)
return r;
- unified = cg_unified();
+ unified = cg_all_unified();
if (unified < 0)
return unified;
if (unified > 0)
return r;
}
- unified = cg_unified();
+ unified = cg_all_unified();
if (unified < 0)
return unified;
if (unified > 0)
if (r < 0)
return r;
- unified = cg_unified();
+ unified = cg_all_unified();
if (unified < 0)
return unified;
if (unified > 0)
* includes controllers we can make sense of and that are
* actually accessible. */
- unified = cg_unified();
+ unified = cg_all_unified();
if (unified < 0)
return unified;
if (unified > 0) {
mask |= CGROUP_CONTROLLER_TO_MASK(v);
}
- /* Currently, we only support the memory, io and pids
+ /* Currently, we support the cpu, memory, io and pids
* controller in the unified hierarchy, mask
* everything else off. */
- mask &= CGROUP_MASK_MEMORY | CGROUP_MASK_IO | CGROUP_MASK_PIDS;
+ mask &= CGROUP_MASK_CPU | CGROUP_MASK_MEMORY | CGROUP_MASK_IO | CGROUP_MASK_PIDS;
} else {
CGroupController c;
}
#endif // 0
-static thread_local int unified_cache = -1;
+static thread_local CGroupUnified unified_cache = CGROUP_UNIFIED_UNKNOWN;
+
+static int cg_update_unified(void) {
-int cg_unified(void) {
struct statfs fs;
/* Checks if we support the unified hierarchy. Returns an
* have any other trouble determining if the unified hierarchy
* is supported. */
- if (unified_cache >= 0)
- return unified_cache;
+ if (unified_cache >= CGROUP_UNIFIED_NONE)
+ return 0;
if (statfs("/sys/fs/cgroup/", &fs) < 0)
return -errno;
#if 0 /// UNNEEDED by elogind
if (F_TYPE_EQUAL(fs.f_type, CGROUP2_SUPER_MAGIC))
- unified_cache = true;
- else if (F_TYPE_EQUAL(fs.f_type, TMPFS_MAGIC))
+ unified_cache = CGROUP_UNIFIED_ALL;
+ else if (F_TYPE_EQUAL(fs.f_type, TMPFS_MAGIC)) {
+ if (statfs("/sys/fs/cgroup/systemd/", &fs) < 0)
+ return -errno;
+
+ unified_cache = F_TYPE_EQUAL(fs.f_type, CGROUP2_SUPER_MAGIC) ?
+ CGROUP_UNIFIED_SYSTEMD : CGROUP_UNIFIED_NONE;
+ } else
+ return -ENOMEDIUM;
#else
/* elogind can not support the unified hierarchy as a controller,
* so always assume a classical hierarchy.
* If, and only *if*, someone really wants to substitute systemd-login
* in an environment managed by systemd with elogind, we might have to
* add such a support. */
- if (F_TYPE_EQUAL(fs.f_type, TMPFS_MAGIC))
+ unified_cache = CGROUP_UNIFIED_NONE;
#endif // 0
- unified_cache = false;
+
+ return 0;
+}
+
+int cg_unified(const char *controller) {
+
+ int r;
+
+ r = cg_update_unified();
+ if (r < 0)
+ return r;
+
+ if (streq_ptr(controller, SYSTEMD_CGROUP_CONTROLLER))
+ return unified_cache >= CGROUP_UNIFIED_SYSTEMD;
else
- return -ENOMEDIUM;
+ return unified_cache >= CGROUP_UNIFIED_ALL;
+}
- return unified_cache;
+int cg_all_unified(void) {
+
+ return cg_unified(NULL);
}
#if 0 /// UNNEEDED by elogind
void cg_unified_flush(void) {
- unified_cache = -1;
+ unified_cache = CGROUP_UNIFIED_UNKNOWN;
}
int cg_enable_everywhere(CGroupMask supported, CGroupMask mask, const char *p) {
if (supported == 0)
return 0;
- unified = cg_unified();
+ unified = cg_all_unified();
if (unified < 0)
return unified;
if (!unified) /* on the legacy hiearchy there's no joining of controllers defined */
/* If the hierarchy is already mounted, then follow whatever
* was chosen for it. */
- unified = cg_unified();
+ unified = cg_all_unified();
if (unified >= 0)
return unified;
bool cg_is_legacy_wanted(void) {
return !cg_is_unified_wanted();
}
+
+bool cg_is_unified_systemd_controller_wanted(void) {
+ static thread_local int wanted = -1;
+ int r, unified;
+
+ /* If the unified hierarchy is requested in full, no need to
+ * bother with this. */
+ if (cg_is_unified_wanted())
+ return 0;
+
+ /* If the hierarchy is already mounted, then follow whatever
+ * was chosen for it. */
+ unified = cg_unified(SYSTEMD_CGROUP_CONTROLLER);
+ if (unified >= 0)
+ return unified;
+
+ /* Otherwise, let's see what the kernel command line has to
+ * say. Since checking that is expensive, let's cache the
+ * result. */
+ if (wanted >= 0)
+ return wanted;
+
+ r = get_proc_cmdline_key("systemd.legacy_systemd_cgroup_controller", NULL);
+ if (r > 0) {
+ if (r > 0)
+ wanted = false;
+ } else {
+ else {
+ _cleanup_free_ char *value = NULL;
+
+ r = get_proc_cmdline_key("systemd.legacy_systemd_cgroup_controller=", &value);
+ if (r < 0)
+ return true;
+
+ if (r == 0)
+ wanted = true;
+ else
+ wanted = parse_boolean(value) <= 0;
+ }
+
+ return wanted;
+}
+
+bool cg_is_legacy_systemd_controller_wanted(void) {
+ return cg_is_legacy_wanted() && !cg_is_unified_systemd_controller_wanted();
+}
#else
bool cg_is_legacy_wanted(void) {
return true;
}
#endif // 0
+bool is_cgroup_fs(const struct statfs *s) {
+ return is_fs_type(s, CGROUP_SUPER_MAGIC) ||
+ is_fs_type(s, CGROUP2_SUPER_MAGIC);
+}
+
+bool fd_is_cgroup_fs(int fd) {
+ struct statfs s;
+
+ if (fstatfs(fd, &s) < 0)
+ return -errno;
+
+ return is_cgroup_fs(&s);
+}
+
static const char *cgroup_controller_table[_CGROUP_CONTROLLER_MAX] = {
[CGROUP_CONTROLLER_CPU] = "cpu",
[CGROUP_CONTROLLER_CPUACCT] = "cpuacct",
#define CGROUP_WEIGHT_MIN UINT64_C(1)
#define CGROUP_WEIGHT_MAX UINT64_C(10000)
#define CGROUP_WEIGHT_DEFAULT UINT64_C(100)
+#endif // 0
#define CGROUP_LIMIT_MIN UINT64_C(0)
#define CGROUP_LIMIT_MAX ((uint64_t) -1)
+#if 0 /// UNNEEDED by elogind
static inline bool CGROUP_WEIGHT_IS_OK(uint64_t x) {
return
x == CGROUP_WEIGHT_INVALID ||
}
#endif // 0
+/* Default resource limits */
+#define DEFAULT_TASKS_MAX_PERCENTAGE 15U /* 15% of PIDs, 4915 on default settings */
+#define DEFAULT_USER_TASKS_MAX_PERCENTAGE 33U /* 33% of PIDs, 10813 on default settings */
+
+typedef enum CGroupUnified {
+ CGROUP_UNIFIED_UNKNOWN = -1,
+ CGROUP_UNIFIED_NONE = 0, /* Both systemd and controllers on legacy */
+ CGROUP_UNIFIED_SYSTEMD = 1, /* Only systemd on unified */
+ CGROUP_UNIFIED_ALL = 2, /* Both systemd and controllers on unified */
+} CGroupUnified;
+
/*
* General rules:
*
int cg_set_attribute(const char *controller, const char *path, const char *attribute, const char *value);
int cg_get_attribute(const char *controller, const char *path, const char *attribute, char **ret);
-
#if 0 /// UNNEEDED by elogind
+int cg_get_keyed_attribute(const char *controller, const char *path, const char *attribute, const char **keys, char **values);
+
int cg_set_group_access(const char *controller, const char *path, mode_t mode, uid_t uid, gid_t gid);
int cg_set_task_access(const char *controller, const char *path, mode_t mode, uid_t uid, gid_t gid);
+
+int cg_set_xattr(const char *controller, const char *path, const char *name, const void *value, size_t size, int flags);
+int cg_get_xattr(const char *controller, const char *path, const char *name, void *value, size_t size);
#endif // 0
int cg_install_release_agent(const char *controller, const char *agent);
#if 0 /// UNNEEDED by elogind
int cg_kernel_controllers(Set *controllers);
+
+bool cg_ns_supported(void);
#endif // 0
-int cg_unified(void);
+int cg_all_unified(void);
+int cg_unified(const char *controller);
#if 0 /// UNNEEDED by elogind
void cg_unified_flush(void);
bool cg_is_unified_wanted(void);
#endif // 0
bool cg_is_legacy_wanted(void);
+#if 0 /// UNNEEDED by elogind
+bool cg_is_unified_systemd_controller_wanted(void);
+bool cg_is_legacy_systemd_controller_wanted(void);
+#endif // 0
const char* cgroup_controller_to_string(CGroupController c) _const_;
CGroupController cgroup_controller_from_string(const char *s) _pure_;
int cg_cpu_shares_parse(const char *s, uint64_t *ret);
int cg_blkio_weight_parse(const char *s, uint64_t *ret);
#endif // 0
+
+bool is_cgroup_fs(const struct statfs *s);
+bool fd_is_cgroup_fs(int fd);
#endif
/* Return a nulstr for a standard cascade of configuration paths,
- * suitable to pass to conf_files_list_nulstr() or config_parse_many()
+ * suitable to pass to conf_files_list_nulstr() or config_parse_many_nulstr()
* to implement drop-in directories for extending configuration
* files. */
#define CONF_PATHS_NULSTR(n) \
assert(remaining > 0);
if (*f != '\\') {
- /* A literal literal, copy verbatim */
+ /* A literal, copy verbatim */
*(t++) = *f;
continue;
}
#include "umask-util.h"
#include "utf8.h"
+#define READ_FULL_BYTES_MAX (4U*1024U*1024U)
+
int write_string_stream(FILE *f, const char *line, bool enforce_newline) {
assert(f);
if (S_ISREG(st.st_mode)) {
/* Safety check */
- if (st.st_size > 4*1024*1024)
+ if (st.st_size > READ_FULL_BYTES_MAX)
return -E2BIG;
/* Start with the right file size, but be prepared for
char *t;
size_t k;
- t = realloc(buf, n+1);
+ t = realloc(buf, n + 1);
if (!t)
return -ENOMEM;
buf = t;
k = fread(buf + l, 1, n - l, f);
+ if (k > 0)
+ l += k;
- if (k <= 0) {
- if (ferror(f))
- return -errno;
+ if (ferror(f))
+ return -errno;
+ if (feof(f))
break;
- }
- l += k;
- n *= 2;
+ /* We aren't expecting fread() to return a short read outside
+ * of (error && eof), assert buffer is full and enlarge buffer.
+ */
+ assert(l == n);
/* Safety check */
- if (n > 4*1024*1024)
+ if (n >= READ_FULL_BYTES_MAX)
return -E2BIG;
+
+ n = MIN(n * 2, READ_FULL_BYTES_MAX);
}
buf[l] = 0;
if (r < 0)
return r;
- fd = mkostemp_safe(t, O_WRONLY|O_CLOEXEC);
+ fd = mkostemp_safe(t);
if (fd < 0) {
free(t);
return -errno;
}
/* This is much like mkostemp() but is subject to umask(). */
-int mkostemp_safe(char *pattern, int flags) {
+int mkostemp_safe(char *pattern) {
_cleanup_umask_ mode_t u = 0;
int fd;
u = umask(077);
- fd = mkostemp(pattern, flags);
+ fd = mkostemp(pattern, O_CLOEXEC);
if (fd < 0)
return -errno;
char *t, *x;
uint64_t u;
unsigned i;
+ int r;
- assert(p);
assert(ret);
/* Turns this:
* /foo/bar/waldo/.#<extra>3c2b6219aa75d7d0
*/
+ if (!p) {
+ r = tmp_dir(&p);
+ if (r < 0)
+ return r;
+ }
+
if (!extra)
extra = "";
int open_tmpfile_unlinkable(const char *directory, int flags) {
char *p;
- int fd;
+ int fd, r;
- if (!directory)
- directory = "/tmp";
+ if (!directory) {
+ r = tmp_dir(&directory);
+ if (r < 0)
+ return r;
+ }
/* Returns an unlinked temporary file that cannot be linked into the file system anymore */
-#ifdef O_TMPFILE
/* Try O_TMPFILE first, if it is supported */
fd = open(directory, flags|O_TMPFILE|O_EXCL, S_IRUSR|S_IWUSR);
if (fd >= 0)
return fd;
-#endif
/* Fall back to unguessable name + unlinking */
p = strjoina(directory, "/systemd-tmp-XXXXXX");
- fd = mkostemp_safe(p, flags);
+ fd = mkostemp_safe(p);
if (fd < 0)
return fd;
* which case "ret_path" will be returned as NULL. If not possible a the tempoary path name used is returned in
* "ret_path". Use link_tmpfile() below to rename the result after writing the file in full. */
-#ifdef O_TMPFILE
{
_cleanup_free_ char *dn = NULL;
log_debug_errno(errno, "Failed to use O_TMPFILE on %s: %m", dn);
}
-#endif
r = tempfn_random(target, NULL, &tmp);
if (r < 0)
int fflush_and_check(FILE *f);
int fopen_temporary(const char *path, FILE **_f, char **_temp_path);
-int mkostemp_safe(char *pattern, int flags);
+int mkostemp_safe(char *pattern);
int tempfn_xxxxxx(const char *p, const char *extra, char **ret);
int tempfn_random(const char *p, const char *extra, char **ret);
}
#if 0 /// UNNEEDED by elogind
-int var_tmp(char **ret) {
- const char *tmp_dir = NULL;
- const char *env_tmp_dir = NULL;
- char *c = NULL;
- int r;
+static int getenv_tmp_dir(const char **ret_path) {
+ const char *n;
+ int r, ret = 0;
- assert(ret);
+ assert(ret_path);
- env_tmp_dir = getenv("TMPDIR");
- if (env_tmp_dir != NULL) {
- r = is_dir(env_tmp_dir, true);
- if (r < 0 && r != -ENOENT)
- return r;
- if (r > 0)
- tmp_dir = env_tmp_dir;
+ /* We use the same order of environment variables python uses in tempfile.gettempdir():
+ * https://docs.python.org/3/library/tempfile.html#tempfile.gettempdir */
+ FOREACH_STRING(n, "TMPDIR", "TEMP", "TMP") {
+ const char *e;
+
+ e = secure_getenv(n);
+ if (!e)
+ continue;
+ if (!path_is_absolute(e)) {
+ r = -ENOTDIR;
+ goto next;
+ }
+ if (!path_is_safe(e)) {
+ r = -EPERM;
+ goto next;
+ }
+
+ r = is_dir(e, true);
+ if (r < 0)
+ goto next;
+ if (r == 0) {
+ r = -ENOTDIR;
+ goto next;
+ }
+
+ *ret_path = e;
+ return 1;
+
+ next:
+ /* Remember first error, to make this more debuggable */
+ if (ret >= 0)
+ ret = r;
}
- if (!tmp_dir)
- tmp_dir = "/var/tmp";
+ if (ret < 0)
+ return ret;
- c = strdup(tmp_dir);
- if (!c)
- return -ENOMEM;
- *ret = c;
+ *ret_path = NULL;
+ return ret;
+}
+
+static int tmp_dir_internal(const char *def, const char **ret) {
+ const char *e;
+ int r, k;
+
+ assert(def);
+ assert(ret);
+ r = getenv_tmp_dir(&e);
+ if (r > 0) {
+ *ret = e;
+ return 0;
+ }
+
+ k = is_dir(def, true);
+ if (k == 0)
+ k = -ENOTDIR;
+ if (k < 0)
+ return r < 0 ? r : k;
+
+ *ret = def;
return 0;
}
+int var_tmp_dir(const char **ret) {
+
+ /* Returns the location for "larger" temporary files, that is backed by physical storage if available, and thus
+ * even might survive a boot: /var/tmp. If $TMPDIR (or related environment variables) are set, its value is
+ * returned preferably however. Note that both this function and tmp_dir() below are affected by $TMPDIR,
+ * making it a variable that overrides all temporary file storage locations. */
+
+ return tmp_dir_internal("/var/tmp", ret);
+}
+
+int tmp_dir(const char **ret) {
+
+ /* Similar to var_tmp_dir() above, but returns the location for "smaller" temporary files, which is usually
+ * backed by an in-memory file system: /tmp. */
+
+ return tmp_dir_internal("/tmp", ret);
+}
+
int inotify_add_watch_fd(int fd, int what, uint32_t mask) {
char path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1];
int r;
return r;
}
+
+int chase_symlinks(const char *path, const char *_root, char **ret) {
+ _cleanup_free_ char *buffer = NULL, *done = NULL, *root = NULL;
+ _cleanup_close_ int fd = -1;
+ unsigned max_follow = 32; /* how many symlinks to follow before giving up and returning ELOOP */
+ char *todo;
+ int r;
+
+ assert(path);
+
+ /* This is a lot like canonicalize_file_name(), but takes an additional "root" parameter, that allows following
+ * symlinks relative to a root directory, instead of the root of the host.
+ *
+ * Note that "root" matters only if we encounter an absolute symlink, it's unused otherwise. Most importantly
+ * this means the path parameter passed in is not prefixed by it.
+ *
+ * Algorithmically this operates on two path buffers: "done" are the components of the path we already
+ * processed and resolved symlinks, "." and ".." of. "todo" are the components of the path we still need to
+ * process. On each iteration, we move one component from "todo" to "done", processing it's special meaning
+ * each time. The "todo" path always starts with at least one slash, the "done" path always ends in no
+ * slash. We always keep an O_PATH fd to the component we are currently processing, thus keeping lookup races
+ * at a minimum. */
+
+ r = path_make_absolute_cwd(path, &buffer);
+ if (r < 0)
+ return r;
+
+ if (_root) {
+ r = path_make_absolute_cwd(_root, &root);
+ if (r < 0)
+ return r;
+ }
+
+ fd = open("/", O_CLOEXEC|O_NOFOLLOW|O_PATH);
+ if (fd < 0)
+ return -errno;
+
+ todo = buffer;
+ for (;;) {
+ _cleanup_free_ char *first = NULL;
+ _cleanup_close_ int child = -1;
+ struct stat st;
+ size_t n, m;
+
+ /* Determine length of first component in the path */
+ n = strspn(todo, "/"); /* The slashes */
+ m = n + strcspn(todo + n, "/"); /* The entire length of the component */
+
+ /* Extract the first component. */
+ first = strndup(todo, m);
+ if (!first)
+ return -ENOMEM;
+
+ todo += m;
+
+ /* Just a single slash? Then we reached the end. */
+ if (isempty(first) || path_equal(first, "/"))
+ break;
+
+ /* Just a dot? Then let's eat this up. */
+ if (path_equal(first, "/."))
+ continue;
+
+ /* Two dots? Then chop off the last bit of what we already found out. */
+ if (path_equal(first, "/..")) {
+ _cleanup_free_ char *parent = NULL;
+ int fd_parent = -1;
+
+ if (isempty(done) || path_equal(done, "/"))
+ return -EINVAL;
+
+ parent = dirname_malloc(done);
+ if (!parent)
+ return -ENOMEM;
+
+ /* Don't allow this to leave the root dir */
+ if (root &&
+ path_startswith(done, root) &&
+ !path_startswith(parent, root))
+ return -EINVAL;
+
+ free_and_replace(done, parent);
+
+ fd_parent = openat(fd, "..", O_CLOEXEC|O_NOFOLLOW|O_PATH);
+ if (fd_parent < 0)
+ return -errno;
+
+ safe_close(fd);
+ fd = fd_parent;
+
+ continue;
+ }
+
+ /* Otherwise let's see what this is. */
+ child = openat(fd, first + n, O_CLOEXEC|O_NOFOLLOW|O_PATH);
+ if (child < 0)
+ return -errno;
+
+ if (fstat(child, &st) < 0)
+ return -errno;
+
+ if (S_ISLNK(st.st_mode)) {
+ _cleanup_free_ char *destination = NULL;
+
+ /* This is a symlink, in this case read the destination. But let's make sure we don't follow
+ * symlinks without bounds. */
+ if (--max_follow <= 0)
+ return -ELOOP;
+
+ r = readlinkat_malloc(fd, first + n, &destination);
+ if (r < 0)
+ return r;
+ if (isempty(destination))
+ return -EINVAL;
+
+ if (path_is_absolute(destination)) {
+
+ /* An absolute destination. Start the loop from the beginning, but use the root
+ * directory as base. */
+
+ safe_close(fd);
+ fd = open(root ?: "/", O_CLOEXEC|O_NOFOLLOW|O_PATH);
+ if (fd < 0)
+ return -errno;
+
+ free_and_replace(buffer, destination);
+
+ todo = buffer;
+ free(done);
+
+ /* Note that we do not revalidate the root, we take it as is. */
+ if (isempty(root))
+ done = NULL;
+ else {
+ done = strdup(root);
+ if (!done)
+ return -ENOMEM;
+ }
+
+ } else {
+ char *joined;
+
+ /* A relative destination. If so, this is what we'll prefix what's left to do with what
+ * we just read, and start the loop again, but remain in the current directory. */
+
+ joined = strjoin("/", destination, todo, NULL);
+ if (!joined)
+ return -ENOMEM;
+
+ free(buffer);
+ todo = buffer = joined;
+ }
+
+ continue;
+ }
+
+ /* If this is not a symlink, then let's just add the name we read to what we already verified. */
+ if (!done) {
+ done = first;
+ first = NULL;
+ } else {
+ if (!strextend(&done, first, NULL))
+ return -ENOMEM;
+ }
+
+ /* And iterate again, but go one directory further down. */
+ safe_close(fd);
+ fd = child;
+ child = -1;
+ }
+
+ if (!done) {
+ /* Special case, turn the empty string into "/", to indicate the root directory. */
+ done = strdup("/");
+ if (!done)
+ return -ENOMEM;
+ }
+
+ *ret = done;
+ done = NULL;
+
+ return 0;
+}
#endif // 0
int get_files_in_directory(const char *path, char ***list);
#if 0 /// UNNEEDED by elogind
-int var_tmp(char **ret);
+int tmp_dir(const char **ret);
+int var_tmp_dir(const char **ret);
#define INOTIFY_EVENT_MAX (sizeof(struct inotify_event) + NAME_MAX + 1)
};
int inotify_add_watch_fd(int fd, int what, uint32_t mask);
+
+int chase_symlinks(const char *path, const char *_root, char **ret);
#endif // 0
char *
utf8_prev_char (const char *p)
{
- while (1)
+ for (;;)
{
p--;
if ((*p & 0xc0) != 0x80)
*(d++) = *p;
dot = false;
}
-
}
if (dot && d > s)
} else { \
if ((_b->name##_prev = _a->name##_prev)) \
_b->name##_prev->name##_next = _b; \
+ else \
+ *_head = _b; \
_b->name##_next = _a; \
_a->name##_prev = _b; \
} \
if (fd < 0)
return -errno;
- fd_inc_sndbuf(fd, SNDBUF_SIZE);
+ (void) fd_inc_sndbuf(fd, SNDBUF_SIZE);
/* We need a blocking fd here since we'd otherwise lose
messages way too early. However, let's not hang forever in the
const char *file,
int line,
const char *func,
- const char *object_field,
- const char *object,
const char *buffer) {
char location[256], prefix[1 + DECIMAL_STR_MAX(int) + 2];
return 0;
if (log_target == LOG_TARGET_CONSOLE_PREFIXED) {
- sprintf(prefix, "<%i>", level);
+ xsprintf(prefix, "<%i>", level);
IOVEC_SET_STRING(iovec[n++], prefix);
}
const char *file,
int line,
const char *func,
- const char *object_field,
- const char *object,
const char *buffer) {
char header_priority[2 + DECIMAL_STR_MAX(int) + 1],
const char *file,
int line,
const char *func,
- const char *object_field,
- const char *object,
const char *buffer) {
char header_priority[2 + DECIMAL_STR_MAX(int) + 1],
int level,
int error,
const char *file, int line, const char *func,
- const char *object_field, const char *object) {
+ const char *object_field, const char *object,
+ const char *extra_field, const char *extra) {
snprintf(header, size,
"PRIORITY=%i\n"
"%s%s%s"
"%s%.*i%s"
"%s%s%s"
+ "%s%s%s"
"SYSLOG_IDENTIFIER=%s\n",
LOG_PRI(level),
LOG_FAC(level),
isempty(object) ? "" : object_field,
isempty(object) ? "" : object,
isempty(object) ? "" : "\n",
+ isempty(extra) ? "" : extra_field,
+ isempty(extra) ? "" : extra,
+ isempty(extra) ? "" : "\n",
program_invocation_short_name);
return 0;
const char *func,
const char *object_field,
const char *object,
+ const char *extra_field,
+ const char *extra,
const char *buffer) {
char header[LINE_MAX];
if (journal_fd < 0)
return 0;
- log_do_header(header, sizeof(header), level, error, file, line, func, object_field, object);
+ log_do_header(header, sizeof(header), level, error, file, line, func, object_field, object, extra_field, extra);
IOVEC_SET_STRING(iovec[0], header);
IOVEC_SET_STRING(iovec[1], "MESSAGE=");
const char *func,
const char *object_field,
const char *object,
+ const char *extra,
+ const char *extra_field,
char *buffer) {
assert(buffer);
+ if (error < 0)
+ error = -error;
+
if (log_target == LOG_TARGET_NULL)
return -error;
if ((level & LOG_FACMASK) == 0)
level = log_facility | LOG_PRI(level);
- if (error < 0)
- error = -error;
-
do {
char *e;
int k = 0;
log_target == LOG_TARGET_JOURNAL_OR_KMSG ||
log_target == LOG_TARGET_JOURNAL) {
- k = write_to_journal(level, error, file, line, func, object_field, object, buffer);
+ k = write_to_journal(level, error, file, line, func, object_field, object, extra_field, extra, buffer);
if (k < 0) {
if (k != -EAGAIN)
log_close_journal();
if (log_target == LOG_TARGET_SYSLOG_OR_KMSG ||
log_target == LOG_TARGET_SYSLOG) {
- k = write_to_syslog(level, error, file, line, func, object_field, object, buffer);
+ k = write_to_syslog(level, error, file, line, func, buffer);
if (k < 0) {
if (k != -EAGAIN)
log_close_syslog();
log_target == LOG_TARGET_JOURNAL_OR_KMSG ||
log_target == LOG_TARGET_KMSG)) {
- k = write_to_kmsg(level, error, file, line, func, object_field, object, buffer);
+ k = write_to_kmsg(level, error, file, line, func, buffer);
if (k < 0) {
log_close_kmsg();
log_open_console();
}
if (k <= 0)
- (void) write_to_console(level, error, file, line, func, object_field, object, buffer);
+ (void) write_to_console(level, error, file, line, func, buffer);
buffer = e;
} while (buffer);
if (_likely_(LOG_PRI(level) > log_max_level))
return -error;
- return log_dispatch(level, error, file, line, func, NULL, NULL, buffer);
+ return log_dispatch(level, error, file, line, func, NULL, NULL, NULL, NULL, buffer);
}
int log_internalv(
vsnprintf(buffer, sizeof(buffer), format, ap);
- return log_dispatch(level, error, file, line, func, NULL, NULL, buffer);
+ return log_dispatch(level, error, file, line, func, NULL, NULL, NULL, NULL, buffer);
}
int log_internal(
const char *func,
const char *object_field,
const char *object,
+ const char *extra_field,
+ const char *extra,
const char *format,
va_list ap) {
vsnprintf(b, l, format, ap);
- return log_dispatch(level, error, file, line, func, object_field, object, buffer);
+ return log_dispatch(level, error, file, line, func, object_field, object, extra_field, extra, buffer);
}
int log_object_internal(
const char *func,
const char *object_field,
const char *object,
+ const char *extra_field,
+ const char *extra,
const char *format, ...) {
va_list ap;
int r;
va_start(ap, format);
- r = log_object_internalv(level, error, file, line, func, object_field, object, format, ap);
+ r = log_object_internalv(level, error, file, line, func, object_field, object, extra_field, extra, format, ap);
va_end(ap);
return r;
return;
DISABLE_WARNING_FORMAT_NONLITERAL;
- xsprintf(buffer, format, text, file, line, func);
+ snprintf(buffer, sizeof buffer, format, text, file, line, func);
REENABLE_WARNING;
log_abort_msg = buffer;
- log_dispatch(level, 0, file, line, func, NULL, NULL, buffer);
+ log_dispatch(level, 0, file, line, func, NULL, NULL, NULL, NULL, buffer);
}
noreturn void log_assert_failed(const char *text, const char *file, int line, const char *func) {
bool fallback = false;
/* If the journal is available do structured logging */
- log_do_header(header, sizeof(header), level, error, file, line, func, NULL, NULL);
+ log_do_header(header, sizeof(header), level, error, file, line, func, NULL, NULL, NULL, NULL);
IOVEC_SET_STRING(iovec[n++], header);
va_start(ap, format);
if (!found)
return -error;
- return log_dispatch(level, error, file, line, func, NULL, NULL, buf + 8);
+ return log_dispatch(level, error, file, line, func, NULL, NULL, NULL, NULL, buf + 8);
}
int log_set_target_from_string(const char *e) {
return 0;
}
-static int parse_proc_cmdline_item(const char *key, const char *value) {
+static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
/*
* The systemd.log_xyz= settings are parsed by all tools, and
/* Only try to read the command line in daemons.
We assume that anything that has a controlling
tty is user stuff. */
- (void) parse_proc_cmdline(parse_proc_cmdline_item);
+ (void) parse_proc_cmdline(parse_proc_cmdline_item, NULL, true);
e = secure_getenv("SYSTEMD_LOG_TARGET");
if (e && log_set_target_from_string(e) < 0)
const char *func,
const char *object_field,
const char *object,
- const char *format, ...) _printf_(8,9);
+ const char *extra_field,
+ const char *extra,
+ const char *format, ...) _printf_(10,11);
int log_object_internalv(
int level,
int error,
- const char*file,
+ const char *file,
int line,
const char *func,
const char *object_field,
const char *object,
+ const char *extra_field,
+ const char *extra,
const char *format,
- va_list ap) _printf_(8,0);
+ va_list ap) _printf_(9,0);
int log_struct_internal(
int level,
#else
# define log_debug_elogind(...) do {} while (0)
#endif // ENABLE_DEBUG_ELOGIND
-
/* Structured logging */
#define log_struct(level, ...) log_struct_internal(level, 0, __FILE__, __LINE__, __func__, __VA_ARGS__)
#define log_struct_errno(level, error, ...) log_struct_internal(level, error, __FILE__, __LINE__, __func__, __VA_ARGS__)
#define CGROUP2_SUPER_MAGIC 0x63677270
#endif
+#ifndef CLONE_NEWCGROUP
+#define CLONE_NEWCGROUP 0x02000000
+#endif
+
#ifndef TMPFS_MAGIC
#define TMPFS_MAGIC 0x01021994
#endif
#define MS_MOVE 8192
#endif
+#ifndef MS_REC
+#define MS_REC 16384
+#endif
+
#ifndef MS_PRIVATE
-#define MS_PRIVATE (1 << 18)
+#define MS_PRIVATE (1<<18)
#endif
-#ifndef SCM_SECURITY
-#define SCM_SECURITY 0x03
+#ifndef MS_REC
+#define MS_REC (1<<19)
+#endif
+
+#ifndef MS_SHARED
+#define MS_SHARED (1<<20)
+#endif
+
+#ifndef MS_RELATIME
+#define MS_RELATIME (1<<21)
+#endif
+
+#ifndef MS_KERNMOUNT
+#define MS_KERNMOUNT (1<<22)
+#endif
+
+#ifndef MS_I_VERSION
+#define MS_I_VERSION (1<<23)
#endif
#ifndef MS_STRICTATIME
-#define MS_STRICTATIME (1<<24)
+#define MS_STRICTATIME (1<<24)
#endif
-#ifndef MS_REC
-#define MS_REC 16384
+#ifndef MS_LAZYTIME
+#define MS_LAZYTIME (1<<25)
#endif
-#ifndef MS_SHARED
-#define MS_SHARED (1<<20)
+#ifndef SCM_SECURITY
+#define SCM_SECURITY 0x03
#endif
#ifndef PR_SET_NO_NEW_PRIVS
# define DRM_IOCTL_DROP_MASTER _IO('d', 0x1f)
#endif
-#if defined(__i386__) || defined(__x86_64__)
-
-/* The precise definition of __O_TMPFILE is arch specific, so let's
- * just define this on x86 where we know the value. */
+/* The precise definition of __O_TMPFILE is arch specific; use the
+ * values defined by the kernel (note: some are hexa, some are octal,
+ * duplicated as-is from the kernel definitions):
+ * - alpha, parisc, sparc: each has a specific value;
+ * - others: they use the "generic" value.
+ */
#ifndef __O_TMPFILE
+#if defined(__alpha__)
+#define __O_TMPFILE 0100000000
+#elif defined(__parisc__) || defined(__hppa__)
+#define __O_TMPFILE 0400000000
+#elif defined(__sparc__) || defined(__sparc64__)
+#define __O_TMPFILE 0x2000000
+#else
#define __O_TMPFILE 020000000
#endif
#define ETHERTYPE_LLDP 0x88cc
#endif
+#ifndef IFA_F_MCAUTOJOIN
+#define IFA_F_MCAUTOJOIN 0x400
+#endif
+
#endif
#include "missing_syscall.h"
return safe_atoi(p, mnt_id);
}
-
int fd_is_mount_point(int fd, const char *filename, int flags) {
union file_handle_union h = FILE_HANDLE_INIT, h_parent = FILE_HANDLE_INIT;
int mount_id = -1, mount_id_parent = -1;
fallback_fdinfo:
r = fd_fdinfo_mnt_id(fd, filename, flags, &mount_id);
- if (r == -EOPNOTSUPP)
+ if (IN_SET(r, -EOPNOTSUPP, -EACCES))
goto fallback_fstat;
if (r < 0)
return r;
continue;
if (umount2(p, flags) < 0) {
- r = -errno;
+ r = log_debug_errno(errno, "Failed to umount %s: %m", p);
continue;
}
+ log_debug("Successfully unmounted %s", p);
+
again = true;
n++;
return 0;
}
-int bind_remount_recursive(const char *prefix, bool ro) {
+int bind_remount_recursive(const char *prefix, bool ro, char **blacklist) {
_cleanup_set_free_free_ Set *done = NULL;
_cleanup_free_ char *cleaned = NULL;
int r;
- /* Recursively remount a directory (and all its submounts)
- * read-only or read-write. If the directory is already
- * mounted, we reuse the mount and simply mark it
- * MS_BIND|MS_RDONLY (or remove the MS_RDONLY for read-write
- * operation). If it isn't we first make it one. Afterwards we
- * apply MS_BIND|MS_RDONLY (or remove MS_RDONLY) to all
- * submounts we can access, too. When mounts are stacked on
- * the same mount point we only care for each individual
- * "top-level" mount on each point, as we cannot
- * influence/access the underlying mounts anyway. We do not
- * have any effect on future submounts that might get
- * propagated, they migt be writable. This includes future
- * submounts that have been triggered via autofs. */
+ /* Recursively remount a directory (and all its submounts) read-only or read-write. If the directory is already
+ * mounted, we reuse the mount and simply mark it MS_BIND|MS_RDONLY (or remove the MS_RDONLY for read-write
+ * operation). If it isn't we first make it one. Afterwards we apply MS_BIND|MS_RDONLY (or remove MS_RDONLY) to
+ * all submounts we can access, too. When mounts are stacked on the same mount point we only care for each
+ * individual "top-level" mount on each point, as we cannot influence/access the underlying mounts anyway. We
+ * do not have any effect on future submounts that might get propagated, they migt be writable. This includes
+ * future submounts that have been triggered via autofs.
+ *
+ * If the "blacklist" parameter is specified it may contain a list of subtrees to exclude from the
+ * remount operation. Note that we'll ignore the blacklist for the top-level path. */
cleaned = strdup(prefix);
if (!cleaned)
if (r < 0)
return r;
+ if (!path_startswith(p, cleaned))
+ continue;
+
+ /* Ignore this mount if it is blacklisted, but only if it isn't the top-level mount we shall
+ * operate on. */
+ if (!path_equal(cleaned, p)) {
+ bool blacklisted = false;
+ char **i;
+
+ STRV_FOREACH(i, blacklist) {
+
+ if (path_equal(*i, cleaned))
+ continue;
+
+ if (!path_startswith(*i, cleaned))
+ continue;
+
+ if (path_startswith(p, *i)) {
+ blacklisted = true;
+ log_debug("Not remounting %s, because blacklisted by %s, called for %s", p, *i, cleaned);
+ break;
+ }
+ }
+ if (blacklisted)
+ continue;
+ }
+
/* Let's ignore autofs mounts. If they aren't
* triggered yet, we want to avoid triggering
* them, as we don't make any guarantees for
continue;
}
- if (path_startswith(p, cleaned) &&
- !set_contains(done, p)) {
-
+ if (!set_contains(done, p)) {
r = set_consume(todo, p);
p = NULL;
-
if (r == -EEXIST)
continue;
if (r < 0)
if (!set_contains(done, cleaned) &&
!set_contains(todo, cleaned)) {
- /* The prefix directory itself is not yet a
- * mount, make it one. */
+ /* The prefix directory itself is not yet a mount, make it one. */
if (mount(cleaned, cleaned, NULL, MS_BIND|MS_REC, NULL) < 0)
return -errno;
if (mount(NULL, prefix, NULL, orig_flags|MS_BIND|MS_REMOUNT|(ro ? MS_RDONLY : 0), NULL) < 0)
return -errno;
+ log_debug("Made top-level directory %s a mount point.", prefix);
+
x = strdup(cleaned);
if (!x)
return -ENOMEM;
if (r < 0)
return r;
- /* Deal with mount points that are obstructed by a
- * later mount */
+ /* Deal with mount points that are obstructed by a later mount */
r = path_is_mount_point(x, 0);
if (r == -ENOENT || r == 0)
continue;
if (mount(NULL, x, NULL, orig_flags|MS_BIND|MS_REMOUNT|(ro ? MS_RDONLY : 0), NULL) < 0)
return -errno;
+ log_debug("Remounted %s read-only.", x);
}
}
}
"glusterfs\0"
"pvfs2\0" /* OrangeFS */
"ocfs2\0"
+ "lustre\0"
;
const char *x;
}
return NULL;
}
+
+#if 0 /// UNNEEDED by elogind
+#define FLAG(name) (flags & name ? STRINGIFY(name) "|" : "")
+static char* mount_flags_to_string(long unsigned flags) {
+ char *x;
+ _cleanup_free_ char *y = NULL;
+ long unsigned overflow;
+
+ overflow = flags & ~(MS_RDONLY |
+ MS_NOSUID |
+ MS_NODEV |
+ MS_NOEXEC |
+ MS_SYNCHRONOUS |
+ MS_REMOUNT |
+ MS_MANDLOCK |
+ MS_DIRSYNC |
+ MS_NOATIME |
+ MS_NODIRATIME |
+ MS_BIND |
+ MS_MOVE |
+ MS_REC |
+ MS_SILENT |
+ MS_POSIXACL |
+ MS_UNBINDABLE |
+ MS_PRIVATE |
+ MS_SLAVE |
+ MS_SHARED |
+ MS_RELATIME |
+ MS_KERNMOUNT |
+ MS_I_VERSION |
+ MS_STRICTATIME |
+ MS_LAZYTIME);
+
+ if (flags == 0 || overflow != 0)
+ if (asprintf(&y, "%lx", overflow) < 0)
+ return NULL;
+
+ x = strjoin(FLAG(MS_RDONLY),
+ FLAG(MS_NOSUID),
+ FLAG(MS_NODEV),
+ FLAG(MS_NOEXEC),
+ FLAG(MS_SYNCHRONOUS),
+ FLAG(MS_REMOUNT),
+ FLAG(MS_MANDLOCK),
+ FLAG(MS_DIRSYNC),
+ FLAG(MS_NOATIME),
+ FLAG(MS_NODIRATIME),
+ FLAG(MS_BIND),
+ FLAG(MS_MOVE),
+ FLAG(MS_REC),
+ FLAG(MS_SILENT),
+ FLAG(MS_POSIXACL),
+ FLAG(MS_UNBINDABLE),
+ FLAG(MS_PRIVATE),
+ FLAG(MS_SLAVE),
+ FLAG(MS_SHARED),
+ FLAG(MS_RELATIME),
+ FLAG(MS_KERNMOUNT),
+ FLAG(MS_I_VERSION),
+ FLAG(MS_STRICTATIME),
+ FLAG(MS_LAZYTIME),
+ y, NULL);
+ if (!x)
+ return NULL;
+ if (!y)
+ x[strlen(x) - 1] = '\0'; /* truncate the last | */
+ return x;
+}
+
+int mount_verbose(
+ int error_log_level,
+ const char *what,
+ const char *where,
+ const char *type,
+ unsigned long flags,
+ const char *options) {
+
+ _cleanup_free_ char *fl = NULL;
+
+ fl = mount_flags_to_string(flags);
+
+ if ((flags & MS_REMOUNT) && !what && !type)
+ log_debug("Remounting %s (%s \"%s\")...",
+ where, strnull(fl), strempty(options));
+ else if (!what && !type)
+ log_debug("Mounting %s (%s \"%s\")...",
+ where, strnull(fl), strempty(options));
+ else if ((flags & MS_BIND) && !type)
+ log_debug("Bind-mounting %s on %s (%s \"%s\")...",
+ what, where, strnull(fl), strempty(options));
+ else
+ log_debug("Mounting %s on %s (%s \"%s\")...",
+ strna(type), where, strnull(fl), strempty(options));
+ if (mount(what, where, type, flags, options) < 0)
+ return log_full_errno(error_log_level, errno,
+ "Failed to mount %s on %s (%s \"%s\"): %m",
+ strna(type), where, strnull(fl), strempty(options));
+ return 0;
+}
+
+int umount_verbose(const char *what) {
+ log_debug("Umounting %s...", what);
+ if (umount(what) < 0)
+ return log_error_errno(errno, "Failed to unmount %s: %m", what);
+ return 0;
+}
+#endif // 0
int repeat_unmount(const char *path, int flags);
int umount_recursive(const char *target, int flags);
-int bind_remount_recursive(const char *prefix, bool ro);
+int bind_remount_recursive(const char *prefix, bool ro, char **blacklist);
int mount_move_root(const char *path);
#endif // 0
const char* mode_to_inaccessible_node(mode_t mode);
#define FILE_HANDLE_INIT { .handle.handle_bytes = MAX_HANDLE_SZ }
+
+#if 0 /// UNNEEDED by elogind
+int mount_verbose(
+ int error_log_level,
+ const char *what,
+ const char *where,
+ const char *type,
+ unsigned long flags,
+ const char *options);
+int umount_verbose(const char *where);
+#endif // 0
return 0;
}
-int parse_percent(const char *p) {
+int parse_percent_unbounded(const char *p) {
const char *pc, *n;
unsigned v;
int r;
r = safe_atou(n, &v);
if (r < 0)
return r;
+
+ return (int) v;
+}
+
+int parse_percent(const char *p) {
+ int v;
+
+ v = parse_percent_unbounded(p);
if (v > 100)
return -ERANGE;
- return (int) v;
+ return v;
}
+
+#if 0 /// UNNEEDED by elogind
+int parse_nice(const char *p, int *ret) {
+ int n, r;
+
+ r = safe_atoi(p, &n);
+ if (r < 0)
+ return r;
+
+ if (!nice_is_valid(n))
+ return -ERANGE;
+
+ *ret = n;
+ return 0;
+}
+#endif // 0
int parse_fractional_part_u(const char **s, size_t digits, unsigned *res);
+int parse_percent_unbounded(const char *p);
int parse_percent(const char *p);
+
+#if 0 /// UNNEEDED by elogind
+int parse_nice(const char *p, int *ret);
+#endif // 0
} else {
/* canonicalized path goes outside of
* prefix, keep the original path instead */
- free(u);
- u = orig;
- orig = NULL;
+ free_and_replace(u, orig);
}
} else
free(t);
assert(path);
assert(prefix);
+ /* Returns a pointer to the start of the first component after the parts matched by
+ * the prefix, iff
+ * - both paths are absolute or both paths are relative,
+ * and
+ * - each component in prefix in turn matches a component in path at the same position.
+ * An empty string will be returned when the prefix and path are equivalent.
+ *
+ * Returns NULL otherwise.
+ */
+
if ((path[0] == '/') != (prefix[0] == '/'))
return NULL;
/* Returns true on paths that refer to a device, either in
* sysfs or in /dev */
- return
- path_startswith(path, "/dev/") ||
- path_startswith(path, "/sys/");
+ return path_startswith(path, "/dev/") ||
+ path_startswith(path, "/sys/");
+}
+
+bool is_deviceallow_pattern(const char *path) {
+ return path_startswith(path, "/dev/") ||
+ startswith(path, "block-") ||
+ startswith(path, "char-");
+}
+
+int systemd_installation_has_version(const char *root, unsigned minimal_version) {
+ const char *pattern;
+ int r;
+
+ /* Try to guess if systemd installation is later than the specified version. This
+ * is hacky and likely to yield false negatives, particularly if the installation
+ * is non-standard. False positives should be relatively rare.
+ */
+
+ NULSTR_FOREACH(pattern,
+ /* /lib works for systems without usr-merge, and for systems with a sane
+ * usr-merge, where /lib is a symlink to /usr/lib. /usr/lib is necessary
+ * for Gentoo which does a merge without making /lib a symlink.
+ */
+ "lib/systemd/libsystemd-shared-*.so\0"
+ "usr/lib/systemd/libsystemd-shared-*.so\0") {
+
+ _cleanup_strv_free_ char **names = NULL;
+ _cleanup_free_ char *path = NULL;
+ char *c, **name;
+
+ path = prefix_root(root, pattern);
+ if (!path)
+ return -ENOMEM;
+
+ r = glob_extend(&names, path);
+ if (r == -ENOENT)
+ continue;
+ if (r < 0)
+ return r;
+
+ assert_se((c = endswith(path, "*.so")));
+ *c = '\0'; /* truncate the glob part */
+
+ STRV_FOREACH(name, names) {
+ /* This is most likely to run only once, hence let's not optimize anything. */
+ char *t, *t2;
+ unsigned version;
+
+ t = startswith(*name, path);
+ if (!t)
+ continue;
+
+ t2 = endswith(t, ".so");
+ if (!t2)
+ continue;
+
+ t2[0] = '\0'; /* truncate the suffix */
+
+ r = safe_atou(t, &version);
+ if (r < 0) {
+ log_debug_errno(r, "Found libsystemd shared at \"%s.so\", but failed to parse version: %m", *name);
+ continue;
+ }
+
+ log_debug("Found libsystemd shared at \"%s.so\", version %u (%s).",
+ *name, version,
+ version >= minimal_version ? "OK" : "too old");
+ if (version >= minimal_version)
+ return true;
+ }
+ }
+
+ return false;
}
#endif // 0
#if 0 /// UNNEEDED by elogind
bool is_device_path(const char *path);
+bool is_deviceallow_pattern(const char *path);
+
+int systemd_installation_has_version(const char *root, unsigned minimal_version);
#endif // 0
return NULL;
free(q->items);
- free(q);
-
- return NULL;
+ return mfree(q);
}
int prioq_ensure_allocated(Prioq **q, compare_func_t compare_func) {
return read_one_line_file("/proc/cmdline", ret);
}
-int parse_proc_cmdline(int (*parse_item)(const char *key, const char *value)) {
+int parse_proc_cmdline(int (*parse_item)(const char *key, const char *value, void *data),
+ void *data,
+ bool strip_prefix) {
_cleanup_free_ char *line = NULL;
const char *p;
int r;
p = line;
for (;;) {
_cleanup_free_ char *word = NULL;
- char *value = NULL;
+ char *value = NULL, *unprefixed;
r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES|EXTRACT_RELAX);
if (r < 0)
/* Filter out arguments that are intended only for the
* initrd */
- if (!in_initrd() && startswith(word, "rd."))
+ unprefixed = startswith(word, "rd.");
+ if (unprefixed && !in_initrd())
continue;
value = strchr(word, '=');
if (value)
*(value++) = 0;
- r = parse_item(word, value);
+ r = parse_item(strip_prefix && unprefixed ? unprefixed : word, value, data);
if (r < 0)
return r;
}
***/
int proc_cmdline(char **ret);
-int parse_proc_cmdline(int (*parse_word)(const char *key, const char *value));
+int parse_proc_cmdline(int (*parse_item)(const char *key, const char *value, void *data),
+ void *data,
+ bool strip_prefix);
int get_proc_cmdline_key(const char *parameter, char **value);
#if 0 /// UNNEEDED by elogind
void valgrind_summary_hack(void);
int pid_compare_func(const void *a, const void *b);
+
+#if 0 /// UNNEEDED by elogind
+static inline bool nice_is_valid(int n) {
+ return n >= PRIO_MIN && n < PRIO_MAX;
+}
+#endif // 0
#include <unistd.h>
//#include "btrfs-util.h"
+#include "cgroup-util.h"
#include "fd-util.h"
#include "log.h"
#include "macro.h"
#include "stat-util.h"
#include "string-util.h"
+static bool is_physical_fs(const struct statfs *sfs) {
+ return !is_temporary_fs(sfs) && !is_cgroup_fs(sfs);
+}
+
int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev) {
_cleanup_closedir_ DIR *d = NULL;
int ret = 0, r;
+ struct statfs sfs;
assert(fd >= 0);
if (!(flags & REMOVE_PHYSICAL)) {
- r = fd_is_temporary_fs(fd);
+ r = fstatfs(fd, &sfs);
if (r < 0) {
safe_close(fd);
- return r;
+ return -errno;
}
- if (!r) {
+ if (is_physical_fs(&sfs)) {
/* We refuse to clean physical file systems
* with this call, unless explicitly
* requested. This is extra paranoia just to
if (statfs(path, &s) < 0)
return -errno;
- if (!is_temporary_fs(&s)) {
+ if (is_physical_fs(&s)) {
log_error("Attempted to remove disk file system, and we can't allow that.");
return -EPERM;
}
#include "hashmap.h"
#include "macro.h"
-Set *internal_set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
-#define set_new(ops) internal_set_new(ops HASHMAP_DEBUG_SRC_ARGS)
+Set *internal_set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
+#define set_new(ops) internal_set_new(ops HASHMAP_DEBUG_SRC_ARGS)
static inline Set *set_free(Set *s) {
internal_hashmap_free(HASHMAP_BASE(s));
return (Set*) internal_hashmap_copy(HASHMAP_BASE(s));
}
-int internal_set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
-#define set_ensure_allocated(h, ops) internal_set_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS)
+int internal_set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
+#define set_ensure_allocated(h, ops) internal_set_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS)
int set_put(Set *s, const void *key);
/* no set_update */
#endif // 0
bool socket_ipv6_is_supported(void) {
- if (access("/proc/net/sockstat6", F_OK) != 0)
+ if (access("/proc/net/if_inet6", F_OK) != 0)
return false;
return true;
}
}
#endif // 0
+
+struct cmsghdr* cmsg_find(struct msghdr *mh, int level, int type, socklen_t length) {
+ struct cmsghdr *cmsg;
+
+ assert(mh);
+
+ CMSG_FOREACH(cmsg, mh)
+ if (cmsg->cmsg_level == level &&
+ cmsg->cmsg_type == type &&
+ (length == (socklen_t) -1 || length == cmsg->cmsg_len))
+ return cmsg;
+
+ return NULL;
+}
+
+#if 0 /// UNNEEDED by elogind
+int socket_ioctl_fd(void) {
+ int fd;
+
+ /* Create a socket to invoke the various network interface ioctl()s on. Traditionally only AF_INET was good for
+ * that. Since kernel 4.6 AF_NETLINK works for this too. We first try to use AF_INET hence, but if that's not
+ * available (for example, because it is made unavailable via SECCOMP or such), we'll fall back to the more
+ * generic AF_NETLINK. */
+
+ fd = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC, 0);
+ if (fd < 0)
+ fd = socket(AF_NETLINK, SOCK_RAW|SOCK_CLOEXEC, NETLINK_GENERIC);
+ if (fd < 0)
+ return -errno;
+
+ return fd;
+}
+#endif // 0
#define CMSG_FOREACH(cmsg, mh) \
for ((cmsg) = CMSG_FIRSTHDR(mh); (cmsg); (cmsg) = CMSG_NXTHDR((mh), (cmsg)))
+struct cmsghdr* cmsg_find(struct msghdr *mh, int level, int type, socklen_t length);
+
/* Covers only file system and abstract AF_UNIX socket addresses, but not unnamed socket addresses. */
#define SOCKADDR_UN_LEN(sa) \
({ \
1 + strnlen(_sa->sun_path+1, sizeof(_sa->sun_path)-1) : \
strnlen(_sa->sun_path, sizeof(_sa->sun_path))); \
})
+
+#if 0 /// UNNEEDED by elogind
+int socket_ioctl_fd(void);
+#endif // 0
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
#include "alloc-util.h"
#include "gunicode.h"
return s;
}
+#if 0 /// UNNEEDED by elogind
char *delete_chars(char *s, const char *bad) {
char *f, *t;
return s;
}
+#endif // 0
char *truncate_nl(char *s) {
assert(s);
return s;
}
+#if 0 /// UNNEEDED by elogind
char ascii_tolower(char x) {
if (x >= 'A' && x <= 'Z')
return x;
}
+char ascii_toupper(char x) {
+
+ if (x >= 'a' && x <= 'z')
+ return x - 'a' + 'A';
+
+ return x;
+}
+
char *ascii_strlower(char *t) {
char *p;
return t;
}
+char *ascii_strupper(char *t) {
+ char *p;
+
+ assert(t);
+
+ for (p = t; *p; p++)
+ *p = ascii_toupper(*p);
+
+ return t;
+}
+
char *ascii_strlower_n(char *t, size_t n) {
size_t i;
return false;
}
+#endif // 0
bool string_has_cc(const char *p, const char *ok) {
const char *t;
if (old_length <= 3 || old_length <= new_length)
return strndup(s, old_length);
- r = new0(char, new_length+1);
+ r = new0(char, new_length+3);
if (!r)
return NULL;
x = new_length - 3;
memcpy(r, s, x);
- r[x] = '.';
- r[x+1] = '.';
- r[x+2] = '.';
+ r[x] = 0xe2; /* tri-dot ellipsis: … */
+ r[x+1] = 0x80;
+ r[x+2] = 0xa6;
memcpy(r + x + 3,
- s + old_length - (new_length - x - 3),
- new_length - x - 3);
+ s + old_length - (new_length - x - 1),
+ new_length - x - 1);
return r;
}
return r;
oom:
- free(r);
- return NULL;
+ return mfree(r);
}
char *strip_tab_ansi(char **ibuf, size_t *_isz) {
if (ferror(f)) {
fclose(f);
- free(obuf);
- return NULL;
+ return mfree(obuf);
}
fclose(f);
return 1;
}
-#pragma GCC push_options
-#pragma GCC optimize("O0")
+/*
+ * Pointer to memset is volatile so that compiler must de-reference
+ * the pointer and can't assume that it points to any function in
+ * particular (such as memset, which it then might further "optimize")
+ * This approach is inspired by openssl's crypto/mem_clr.c.
+ */
+typedef void *(*memset_t)(void *,int,size_t);
-void* memory_erase(void *p, size_t l) {
- volatile uint8_t* x = (volatile uint8_t*) p;
-
- /* This basically does what memset() does, but hopefully isn't
- * optimized away by the compiler. One of those days, when
- * glibc learns memset_s() we should replace this call by
- * memset_s(), but until then this has to do. */
-
- for (; l > 0; l--)
- *(x++) = 'x';
+static volatile memset_t memset_func = memset;
- return p;
+void* memory_erase(void *p, size_t l) {
+ return memset_func(p, 'x', l);
}
-#pragma GCC pop_options
-
char* string_erase(char *x) {
if (!x)
return !p || !p[0];
}
+#if 0 /// UNNEEDED by elogind
static inline const char *empty_to_null(const char *p) {
return isempty(p) ? NULL : p;
}
+static inline const char *strdash_if_empty(const char *str) {
+ return isempty(str) ? "-" : str;
+}
+#endif // 0
+
static inline char *startswith(const char *s, const char *prefix) {
size_t l;
return NULL;
}
+#if 0 /// UNNEEDED by elogind
static inline char *startswith_no_case(const char *s, const char *prefix) {
size_t l;
return NULL;
}
+#endif // 0
char *endswith(const char *s, const char *postfix) _pure_;
char *endswith_no_case(const char *s, const char *postfix) _pure_;
})
char *strstrip(char *s);
+#if 0 /// UNNEEDED by elogind
char *delete_chars(char *s, const char *bad);
+#endif // 0
char *truncate_nl(char *s);
+#if 0 /// UNNEEDED by elogind
char ascii_tolower(char x);
char *ascii_strlower(char *s);
char *ascii_strlower_n(char *s, size_t n);
+char ascii_toupper(char x);
+char *ascii_strupper(char *s);
+
int ascii_strcasecmp_n(const char *a, const char *b, size_t n);
int ascii_strcasecmp_nn(const char *a, size_t n, const char *b, size_t m);
bool chars_intersect(const char *a, const char *b) _pure_;
+#endif // 0
static inline bool _pure_ in_charset(const char *s, const char* charset) {
assert(s);
char **strv_free(char **l) {
strv_clear(l);
- free(l);
- return NULL;
+ return mfree(l);
}
char **strv_free_erase(char **l) {
return buf;
oom:
- free(buf);
- return NULL;
+ return mfree(buf);
}
#endif // 0
for (i = l; *i; i++)
strv_free(*i);
- free(l);
- return NULL;
+ return mfree(l);
}
char **strv_skip(char **l, size_t n) {
#define STRV_FOREACH(s, l) \
for ((s) = (l); (s) && *(s); (s)++)
-#define STRV_FOREACH_BACKWARDS(s, l) \
- STRV_FOREACH(s, l) \
- ; \
- for ((s)--; (l) && ((s) >= (l)); (s)--)
+#define STRV_FOREACH_BACKWARDS(s, l) \
+ for (s = ({ \
+ char **_l = l; \
+ _l ? _l + strv_length(_l) - 1U : NULL; \
+ }); \
+ (l) && ((s) >= (l)); \
+ (s)--)
#define STRV_FOREACH_PAIR(x, y, l) \
for ((x) = (l), (y) = (x+1); (x) && *(x) && *(y); (x) += 2, (y) = (x + 1))
})
#define STR_IN_SET(x, ...) strv_contains(STRV_MAKE(__VA_ARGS__), x)
+#define STRPTR_IN_SET(x, ...) \
+ ({ \
+ const char* _x = (x); \
+ _x && strv_contains(STRV_MAKE(__VA_ARGS__), _x); \
+ })
#define FOREACH_STRING(x, ...) \
for (char **_l = ({ \
}
r = isatty(fd);
- if (r < 0) {
- safe_close(fd);
- return -errno;
- }
-
- if (!r) {
+ if (r == 0) {
safe_close(fd);
return -ENOTTY;
}
}
const char *default_term_for_tty(const char *tty) {
- return tty && tty_is_vc_resolve(tty) ? "TERM=linux" : "TERM=vt220";
+ return tty && tty_is_vc_resolve(tty) ? "linux" : "vt220";
}
#endif // 0
static int enabled = -1;
if (_unlikely_(enabled < 0)) {
- const char *colors;
+#if 0 /// elogind does not allow such forcing, and we are never init!
+ int val;
- colors = getenv("SYSTEMD_COLORS");
- if (colors)
- enabled = parse_boolean(colors) != 0;
+ val = getenv_bool("SYSTEMD_COLORS");
+ if (val >= 0)
+ enabled = val;
else if (getpid() == 1)
/* PID1 outputs to the console without holding it open all the time */
enabled = !getenv_terminal_is_dumb();
else
+#endif // 0
enabled = !terminal_is_dumb();
}
#define ANSI_HIGHLIGHT_YELLOW "\x1B[0;1;33m"
#define ANSI_HIGHLIGHT_BLUE "\x1B[0;1;34m"
#define ANSI_HIGHLIGHT_UNDERLINE "\x1B[0;1;4m"
+#define ANSI_HIGHLIGHT_RED_UNDERLINE "\x1B[0;1;4;31m"
+#define ANSI_HIGHLIGHT_GREEN_UNDERLINE "\x1B[0;1;4;32m"
+#define ANSI_HIGHLIGHT_YELLOW_UNDERLINE "\x1B[0;1;4;33m"
+#define ANSI_HIGHLIGHT_BLUE_UNDERLINE "\x1B[0;1;4;34m"
#define ANSI_NORMAL "\x1B[0m"
#define ANSI_ERASE_TO_END_OF_LINE "\x1B[K"
bool terminal_is_dumb(void);
bool colors_enabled(void);
-static inline const char *ansi_underline(void) {
- return colors_enabled() ? ANSI_UNDERLINE : "";
-}
-
-static inline const char *ansi_highlight(void) {
- return colors_enabled() ? ANSI_HIGHLIGHT : "";
-}
-
-static inline const char *ansi_highlight_underline(void) {
- return colors_enabled() ? ANSI_HIGHLIGHT_UNDERLINE : "";
-}
-
-static inline const char *ansi_highlight_red(void) {
- return colors_enabled() ? ANSI_HIGHLIGHT_RED : "";
-}
-
-static inline const char *ansi_highlight_green(void) {
- return colors_enabled() ? ANSI_HIGHLIGHT_GREEN : "";
-}
-
-static inline const char *ansi_highlight_yellow(void) {
- return colors_enabled() ? ANSI_HIGHLIGHT_YELLOW : "";
-}
-
-static inline const char *ansi_highlight_blue(void) {
- return colors_enabled() ? ANSI_HIGHLIGHT_BLUE : "";
-}
-
-static inline const char *ansi_normal(void) {
- return colors_enabled() ? ANSI_NORMAL : "";
-}
+#define DEFINE_ANSI_FUNC(name, NAME) \
+ static inline const char *ansi_##name(void) { \
+ return colors_enabled() ? ANSI_##NAME : ""; \
+ } \
+ struct __useless_struct_to_allow_trailing_semicolon__
+
+DEFINE_ANSI_FUNC(underline, UNDERLINE);
+DEFINE_ANSI_FUNC(highlight, HIGHLIGHT);
+DEFINE_ANSI_FUNC(highlight_underline, HIGHLIGHT_UNDERLINE);
+DEFINE_ANSI_FUNC(highlight_red, HIGHLIGHT_RED);
+DEFINE_ANSI_FUNC(highlight_green, HIGHLIGHT_GREEN);
+DEFINE_ANSI_FUNC(highlight_yellow, HIGHLIGHT_YELLOW);
+DEFINE_ANSI_FUNC(highlight_blue, HIGHLIGHT_BLUE);
+DEFINE_ANSI_FUNC(highlight_red_underline, HIGHLIGHT_RED_UNDERLINE);
+DEFINE_ANSI_FUNC(highlight_green_underline, HIGHLIGHT_GREEN_UNDERLINE);
+DEFINE_ANSI_FUNC(highlight_yellow_underline, HIGHLIGHT_YELLOW_UNDERLINE);
+DEFINE_ANSI_FUNC(highlight_blue_underline, HIGHLIGHT_BLUE_UNDERLINE);
+DEFINE_ANSI_FUNC(normal, NORMAL);
int get_ctty_devnr(pid_t pid, dev_t *d);
int get_ctty(pid_t, dev_t *_devnr, char **r);
#include "strv.h"
#include "time-util.h"
-#if 0 /// UNNEEDED by elogind
-static nsec_t timespec_load_nsec(const struct timespec *ts);
-#endif // 0
-
static clockid_t map_clock_id(clockid_t c) {
/* Some more exotic archs (s390, ppc, …) lack the "ALARM" flavour of the clocks. Thus, clock_gettime() will
}
#if 0 /// UNNEEDED by elogind
-static nsec_t timespec_load_nsec(const struct timespec *ts) {
+nsec_t timespec_load_nsec(const struct timespec *ts) {
assert(ts);
if (ts->tv_sec == (time_t) -1 && ts->tv_nsec == (long) -1)
return tv;
}
-static char *format_timestamp_internal(char *buf, size_t l, usec_t t,
- bool utc, bool us) {
+static char *format_timestamp_internal(
+ char *buf,
+ size_t l,
+ usec_t t,
+ bool utc,
+ bool us) {
+
+ /* The weekdays in non-localized (English) form. We use this instead of the localized form, so that our
+ * generated timestamps may be parsed with parse_timestamp(), and always read the same. */
+ static const char * const weekdays[] = {
+ [0] = "Sun",
+ [1] = "Mon",
+ [2] = "Tue",
+ [3] = "Wed",
+ [4] = "Thu",
+ [5] = "Fri",
+ [6] = "Sat",
+ };
+
struct tm tm;
time_t sec;
- int k;
+ size_t n;
assert(buf);
- assert(l > 0);
+ if (l <
+ 3 + /* week day */
+ 1 + 10 + /* space and date */
+ 1 + 8 + /* space and time */
+ (us ? 1 + 6 : 0) + /* "." and microsecond part */
+ 1 + 1 + /* space and shortest possible zone */
+ 1)
+ return NULL; /* Not enough space even for the shortest form. */
if (t <= 0 || t == USEC_INFINITY)
+ return NULL; /* Timestamp is unset */
+
+ sec = (time_t) (t / USEC_PER_SEC); /* Round down */
+ if ((usec_t) sec != (t / USEC_PER_SEC))
+ return NULL; /* overflow? */
+
+ if (!localtime_or_gmtime_r(&sec, &tm, utc))
return NULL;
- sec = (time_t) (t / USEC_PER_SEC);
- localtime_or_gmtime_r(&sec, &tm, utc);
+ /* Start with the week day */
+ assert((size_t) tm.tm_wday < ELEMENTSOF(weekdays));
+ memcpy(buf, weekdays[tm.tm_wday], 4);
- if (us)
- k = strftime(buf, l, "%a %Y-%m-%d %H:%M:%S", &tm);
- else
- k = strftime(buf, l, "%a %Y-%m-%d %H:%M:%S %Z", &tm);
+ /* Add the main components */
+ if (strftime(buf + 3, l - 3, " %Y-%m-%d %H:%M:%S", &tm) <= 0)
+ return NULL; /* Doesn't fit */
- if (k <= 0)
- return NULL;
+ /* Append the microseconds part, if that's requested */
if (us) {
- snprintf(buf + strlen(buf), l - strlen(buf), ".%06llu", (unsigned long long) (t % USEC_PER_SEC));
- if (strftime(buf + strlen(buf), l - strlen(buf), " %Z", &tm) <= 0)
- return NULL;
+ n = strlen(buf);
+ if (n + 8 > l)
+ return NULL; /* Microseconds part doesn't fit. */
+
+ sprintf(buf + n, ".%06llu", (unsigned long long) (t % USEC_PER_SEC));
+ }
+
+ /* Append the timezone */
+ n = strlen(buf);
+ if (utc) {
+ /* If this is UTC then let's explicitly use the "UTC" string here, because gmtime_r() normally uses the
+ * obsolete "GMT" instead. */
+ if (n + 5 > l)
+ return NULL; /* "UTC" doesn't fit. */
+
+ strcpy(buf + n, " UTC");
+
+ } else if (!isempty(tm.tm_zone)) {
+ size_t tn;
+
+ /* An explicit timezone is specified, let's use it, if it fits */
+ tn = strlen(tm.tm_zone);
+ if (n + 1 + tn + 1 > l) {
+ /* The full time zone does not fit in. Yuck. */
+
+ if (n + 1 + _POSIX_TZNAME_MAX + 1 > l)
+ return NULL; /* Not even enough space for the POSIX minimum (of 6)? In that case, complain that it doesn't fit */
+
+ /* So the time zone doesn't fit in fully, but the caller passed enough space for the POSIX
+ * minimum time zone length. In this case suppress the timezone entirely, in order not to dump
+ * an overly long, hard to read string on the user. This should be safe, because the user will
+ * assume the local timezone anyway if none is shown. And so does parse_timestamp(). */
+ } else {
+ buf[n++] = ' ';
+ strcpy(buf + n, tm.tm_zone);
+ }
}
return buf;
{ "Sat", 6 },
};
- const char *k;
- const char *utc;
+ const char *k, *utc, *tzn = NULL;
struct tm tm, copy;
time_t x;
usec_t x_usec, plus = 0, minus = 0, ret;
- int r, weekday = -1;
+ int r, weekday = -1, dst = -1;
unsigned i;
/*
goto finish;
}
+ /* See if the timestamp is suffixed with UTC */
utc = endswith_no_case(t, " UTC");
if (utc)
t = strndupa(t, utc - t);
+ else {
+ const char *e = NULL;
+ int j;
+
+ tzset();
+
+ /* See if the timestamp is suffixed by either the DST or non-DST local timezone. Note that we only
+ * support the local timezones here, nothing else. Not because we wouldn't want to, but simply because
+ * there are no nice APIs available to cover this. By accepting the local time zone strings, we make
+ * sure that all timestamps written by format_timestamp() can be parsed correctly, even though we don't
+ * support arbitrary timezone specifications. */
+
+ for (j = 0; j <= 1; j++) {
- x = ret / USEC_PER_SEC;
+ if (isempty(tzname[j]))
+ continue;
+
+ e = endswith_no_case(t, tzname[j]);
+ if (!e)
+ continue;
+ if (e == t)
+ continue;
+ if (e[-1] != ' ')
+ continue;
+
+ break;
+ }
+
+ if (IN_SET(j, 0, 1)) {
+ /* Found one of the two timezones specified. */
+ t = strndupa(t, e - t - 1);
+ dst = j;
+ tzn = tzname[j];
+ }
+ }
+
+ x = (time_t) (ret / USEC_PER_SEC);
x_usec = 0;
- assert_se(localtime_or_gmtime_r(&x, &tm, utc));
- tm.tm_isdst = -1;
+ if (!localtime_or_gmtime_r(&x, &tm, utc))
+ return -EINVAL;
+
+ tm.tm_isdst = dst;
+ if (tzn)
+ tm.tm_zone = tzn;
if (streq(t, "today")) {
tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
goto from_tm;
}
-
for (i = 0; i < ELEMENTSOF(day_nr); i++) {
size_t skip;
return -EINVAL;
x_usec = add;
-
}
from_tm:
#define USEC_PER_YEAR ((usec_t) (31557600ULL*USEC_PER_SEC))
#define NSEC_PER_YEAR ((nsec_t) (31557600ULL*NSEC_PER_SEC))
-#define FORMAT_TIMESTAMP_MAX ((4*4+1)+11+9+4+1) /* weekdays can be unicode */
+/* We assume a maximum timezone length of 6. TZNAME_MAX is not defined on Linux, but glibc internally initializes this
+ * to 6. Let's rely on that. */
+#define FORMAT_TIMESTAMP_MAX (3+1+10+1+8+1+6+1+6+1)
#define FORMAT_TIMESTAMP_WIDTH 28 /* when outputting, assume this width */
#define FORMAT_TIMESTAMP_RELATIVE_MAX 256
#define FORMAT_TIMESPAN_MAX 64
usec_t triple_timestamp_by_clock(triple_timestamp *ts, clockid_t clock);
usec_t timespec_load(const struct timespec *ts) _pure_;
+#if 0 /// UNNEEDED by elogind
+nsec_t timespec_load_nsec(const struct timespec *ts) _pure_;
+#endif // 0
struct timespec *timespec_store(struct timespec *ts, usec_t u);
usec_t timeval_load(const struct timeval *tv) _pure_;
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
+#include <utmp.h>
-#include "missing.h"
#include "alloc-util.h"
#include "fd-util.h"
+#include "fileio.h"
#include "formats-util.h"
#include "macro.h"
+#include "missing.h"
#include "parse-util.h"
#include "path-util.h"
#include "string-util.h"
+#include "strv.h"
#include "user-util.h"
+#include "utf8.h"
bool uid_is_valid(uid_t uid) {
return 0;
}
+#if 0 /// UNNEEDED by elogind
+int get_user_creds_clean(
+ const char **username,
+ uid_t *uid, gid_t *gid,
+ const char **home,
+ const char **shell) {
+
+ int r;
+
+ /* Like get_user_creds(), but resets home/shell to NULL if they don't contain anything relevant. */
+
+ r = get_user_creds(username, uid, gid, home, shell);
+ if (r < 0)
+ return r;
+
+ if (shell &&
+ (isempty(*shell) || PATH_IN_SET(*shell,
+ "/bin/nologin",
+ "/sbin/nologin",
+ "/usr/bin/nologin",
+ "/usr/sbin/nologin")))
+ *shell = NULL;
+
+ if (home &&
+ (isempty(*home) || path_equal(*home, "/")))
+ *home = NULL;
+
+ return 0;
+}
+
int get_group_creds(const char **groupname, gid_t *gid) {
struct group *g;
gid_t id;
return 0;
}
+#endif // 0
char* uid_to_name(uid_t uid) {
char *ret;
#endif // 0
int reset_uid_gid(void) {
+ int r;
- if (setgroups(0, NULL) < 0)
- return -errno;
+ r = maybe_setgroups(0, NULL);
+ if (r < 0)
+ return r;
if (setresgid(0, 0, 0) < 0)
return -errno;
return fd;
}
#endif // 0
+
+bool valid_user_group_name(const char *u) {
+ const char *i;
+ long sz;
+
+ /* Checks if the specified name is a valid user/group name. */
+
+ if (isempty(u))
+ return false;
+
+ if (!(u[0] >= 'a' && u[0] <= 'z') &&
+ !(u[0] >= 'A' && u[0] <= 'Z') &&
+ u[0] != '_')
+ return false;
+
+ for (i = u+1; *i; i++) {
+ if (!(*i >= 'a' && *i <= 'z') &&
+ !(*i >= 'A' && *i <= 'Z') &&
+ !(*i >= '0' && *i <= '9') &&
+ *i != '_' &&
+ *i != '-')
+ return false;
+ }
+
+ sz = sysconf(_SC_LOGIN_NAME_MAX);
+ assert_se(sz > 0);
+
+ if ((size_t) (i-u) > (size_t) sz)
+ return false;
+
+ if ((size_t) (i-u) > UT_NAMESIZE - 1)
+ return false;
+
+ return true;
+}
+
+bool valid_user_group_name_or_id(const char *u) {
+
+ /* Similar as above, but is also fine with numeric UID/GID specifications, as long as they are in the right
+ * range, and not the invalid user ids. */
+
+ if (isempty(u))
+ return false;
+
+ if (valid_user_group_name(u))
+ return true;
+
+ return parse_uid(u, NULL) >= 0;
+}
+
+bool valid_gecos(const char *d) {
+
+ if (!d)
+ return false;
+
+ if (!utf8_is_valid(d))
+ return false;
+
+ if (string_has_cc(d, NULL))
+ return false;
+
+ /* Colons are used as field separators, and hence not OK */
+ if (strchr(d, ':'))
+ return false;
+
+ return true;
+}
+
+bool valid_home(const char *p) {
+
+ if (isempty(p))
+ return false;
+
+ if (!utf8_is_valid(p))
+ return false;
+
+ if (string_has_cc(p, NULL))
+ return false;
+
+ if (!path_is_absolute(p))
+ return false;
+
+ if (!path_is_safe(p))
+ return false;
+
+ /* Colons are used as field separators, and hence not OK */
+ if (strchr(p, ':'))
+ return false;
+
+ return true;
+}
+
+int maybe_setgroups(size_t size, const gid_t *list) {
+ int r;
+
+ /* Check if setgroups is allowed before we try to drop all the auxiliary groups */
+ if (size == 0) { /* Dropping all aux groups? */
+ _cleanup_free_ char *setgroups_content = NULL;
+ bool can_setgroups;
+
+ r = read_one_line_file("/proc/self/setgroups", &setgroups_content);
+ if (r == -ENOENT)
+ /* Old kernels don't have /proc/self/setgroups, so assume we can use setgroups */
+ can_setgroups = true;
+ else if (r < 0)
+ return r;
+ else
+ can_setgroups = streq(setgroups_content, "allow");
+
+ if (!can_setgroups) {
+ log_debug("Skipping setgroups(), /proc/self/setgroups is set to 'deny'");
+ return 0;
+ }
+ }
+
+ if (setgroups(size, list) < 0)
+ return -errno;
+
+ return 0;
+}
***/
#include <stdbool.h>
+#include <stdint.h>
#include <sys/types.h>
#include <unistd.h>
#endif // 0
int get_user_creds(const char **username, uid_t *uid, gid_t *gid, const char **home, const char **shell);
+#if 0 /// UNNEEDED by elogind
+int get_user_creds_clean(const char **username, uid_t *uid, gid_t *gid, const char **home, const char **shell);
int get_group_creds(const char **groupname, gid_t *gid);
+#endif // 0
char* uid_to_name(uid_t uid);
char* gid_to_name(gid_t gid);
#define UID_INVALID ((uid_t) -1)
#define GID_INVALID ((gid_t) -1)
-/* The following macros add 1 when converting things, since UID 0 is a
- * valid UID, while the pointer NULL is special */
+/* Let's pick a UIDs within the 16bit range, so that we are compatible with containers using 16bit
+ * user namespacing. At least on Fedora normal users are allocated until UID 60000, hence do not
+ * allocate from below this. Also stay away from the upper end of the range as that is often used
+ * for overflow/nobody users. */
+#define DYNAMIC_UID_MIN ((uid_t) UINT32_C(0x0000EF00))
+#define DYNAMIC_UID_MAX ((uid_t) UINT32_C(0x0000FFEF))
+
+static inline bool uid_is_dynamic(uid_t uid) {
+ return DYNAMIC_UID_MIN <= uid && uid <= DYNAMIC_UID_MAX;
+}
+
+/* The following macros add 1 when converting things, since UID 0 is a valid UID, while the pointer
+ * NULL is special */
#define PTR_TO_UID(p) ((uid_t) (((uintptr_t) (p))-1))
#define UID_TO_PTR(u) ((void*) (((uintptr_t) (u))+1))
static inline bool userns_supported(void) {
return access("/proc/self/uid_map", F_OK) >= 0;
}
+
+bool valid_user_group_name(const char *u);
+bool valid_user_group_name_or_id(const char *u);
+bool valid_gecos(const char *d);
+bool valid_home(const char *p);
+
+int maybe_setgroups(size_t size, const gid_t *list);
* 2. the root file system must be a memory file system
*
* The second check is extra paranoia, since misdetecting an
- * initrd can have bad bad consequences due the initrd
+ * initrd can have bad consequences due the initrd
* emptying when transititioning to the main systemd.
*/
return b ? "1" : "0";
}
+static inline const char* enable_disable(bool b) {
+ return b ? "enable" : "disable";
+}
+
void execute_directories(const char* const* directories, usec_t timeout, char *argv[]);
#if 0 /// UNNEEDED by elogind
{ "VMwareVMware", VIRTUALIZATION_VMWARE },
/* http://msdn.microsoft.com/en-us/library/ff542428.aspx */
{ "Microsoft Hv", VIRTUALIZATION_MICROSOFT },
+ /* https://wiki.freebsd.org/bhyve */
+ { "bhyve bhyve ", VIRTUALIZATION_BHYVE },
};
uint32_t eax, ecx;
{ "Xen", VIRTUALIZATION_XEN },
{ "Bochs", VIRTUALIZATION_BOCHS },
{ "Parallels", VIRTUALIZATION_PARALLELS },
+ /* https://wiki.freebsd.org/bhyve */
+ { "BHYVE", VIRTUALIZATION_BHYVE },
};
unsigned i;
int r;
return r;
}
+
+static int userns_has_mapping(const char *name) {
+ _cleanup_fclose_ FILE *f = NULL;
+ _cleanup_free_ char *buf = NULL;
+ size_t n_allocated = 0;
+ ssize_t n;
+ uint32_t a, b, c;
+ int r;
+
+ f = fopen(name, "re");
+ if (!f) {
+ log_debug_errno(errno, "Failed to open %s: %m", name);
+ return errno == -ENOENT ? false : -errno;
+ }
+
+ n = getline(&buf, &n_allocated, f);
+ if (n < 0) {
+ if (feof(f)) {
+ log_debug("%s is empty, we're in an uninitialized user namespace", name);
+ return true;
+ }
+
+ return log_debug_errno(errno, "Failed to read %s: %m", name);
+ }
+
+ r = sscanf(buf, "%"PRIu32" %"PRIu32" %"PRIu32, &a, &b, &c);
+ if (r < 3)
+ return log_debug_errno(errno, "Failed to parse %s: %m", name);
+
+ if (a == 0 && b == 0 && c == UINT32_MAX) {
+ /* The kernel calls mappings_overlap() and does not allow overlaps */
+ log_debug("%s has a full 1:1 mapping", name);
+ return false;
+ }
+
+ /* Anything else implies that we are in a user namespace */
+ log_debug("Mapping found in %s, we're in a user namespace", name);
+ return true;
+}
+
+int running_in_userns(void) {
+ _cleanup_free_ char *line = NULL;
+ int r;
+
+ r = userns_has_mapping("/proc/self/uid_map");
+ if (r != 0)
+ return r;
+
+ r = userns_has_mapping("/proc/self/gid_map");
+ if (r != 0)
+ return r;
+
+ /* "setgroups" file was added in kernel v3.18-rc6-15-g9cc46516dd. It is also
+ * possible to compile a kernel without CONFIG_USER_NS, in which case "setgroups"
+ * also does not exist. We cannot distinguish those two cases, so assume that
+ * we're running on a stripped-down recent kernel, rather than on an old one,
+ * and if the file is not found, return false.
+ */
+ r = read_one_line_file("/proc/self/setgroups", &line);
+ if (r < 0) {
+ log_debug_errno(r, "/proc/self/setgroups: %m");
+ return r == -ENOENT ? false : r;
+ }
+
+ truncate_nl(line);
+ r = streq(line, "deny");
+ /* See user_namespaces(7) for a description of this "setgroups" contents. */
+ log_debug("/proc/self/setgroups contains \"%s\", %s user namespace", line, r ? "in" : "not in");
+ return r;
+}
#endif // 0
int running_in_chroot(void) {
int ret;
+#if 0 /// elogind does not allow to ignore chroots, we are never init!
+ if (getenv_bool("SYSTEMD_IGNORE_CHROOT") > 0)
+ return 0;
+#endif // 0
+
ret = files_same("/proc/1/root", "/");
if (ret < 0)
return ret;
[VIRTUALIZATION_MICROSOFT] = "microsoft",
[VIRTUALIZATION_ZVM] = "zvm",
[VIRTUALIZATION_PARALLELS] = "parallels",
+ [VIRTUALIZATION_BHYVE] = "bhyve",
[VIRTUALIZATION_VM_OTHER] = "vm-other",
[VIRTUALIZATION_SYSTEMD_NSPAWN] = "systemd-nspawn",
VIRTUALIZATION_MICROSOFT,
VIRTUALIZATION_ZVM,
VIRTUALIZATION_PARALLELS,
+ VIRTUALIZATION_BHYVE,
VIRTUALIZATION_VM_OTHER,
VIRTUALIZATION_VM_LAST = VIRTUALIZATION_VM_OTHER,
int detect_container(void);
#if 0 /// UNNEEDED by elogind
int detect_virtualization(void);
-#endif // 0
+int running_in_userns(void);
+#endif // 0
int running_in_chroot(void);
const char *virtualization_to_string(int v) _const_;
/* Initialize everything to the kernel defaults, assuming the
* structure is preinitialized to 0 */
+ c->cpu_weight = CGROUP_WEIGHT_INVALID;
+ c->startup_cpu_weight = CGROUP_WEIGHT_INVALID;
+ c->cpu_quota_per_sec_usec = USEC_INFINITY;
+
c->cpu_shares = CGROUP_CPU_SHARES_INVALID;
c->startup_cpu_shares = CGROUP_CPU_SHARES_INVALID;
- c->cpu_quota_per_sec_usec = USEC_INFINITY;
c->memory_high = CGROUP_LIMIT_MAX;
c->memory_max = CGROUP_LIMIT_MAX;
+ c->memory_swap_max = CGROUP_LIMIT_MAX;
c->memory_limit = CGROUP_LIMIT_MAX;
"%sBlockIOAccounting=%s\n"
"%sMemoryAccounting=%s\n"
"%sTasksAccounting=%s\n"
+ "%sCPUWeight=%" PRIu64 "\n"
+ "%sStartupCPUWeight=%" PRIu64 "\n"
"%sCPUShares=%" PRIu64 "\n"
"%sStartupCPUShares=%" PRIu64 "\n"
"%sCPUQuotaPerSecSec=%s\n"
"%sMemoryLow=%" PRIu64 "\n"
"%sMemoryHigh=%" PRIu64 "\n"
"%sMemoryMax=%" PRIu64 "\n"
+ "%sMemorySwapMax=%" PRIu64 "\n"
"%sMemoryLimit=%" PRIu64 "\n"
"%sTasksMax=%" PRIu64 "\n"
"%sDevicePolicy=%s\n"
prefix, yes_no(c->blockio_accounting),
prefix, yes_no(c->memory_accounting),
prefix, yes_no(c->tasks_accounting),
+ prefix, c->cpu_weight,
+ prefix, c->startup_cpu_weight,
prefix, c->cpu_shares,
prefix, c->startup_cpu_shares,
prefix, format_timespan(u, sizeof(u), c->cpu_quota_per_sec_usec, 1),
prefix, c->memory_low,
prefix, c->memory_high,
prefix, c->memory_max,
+ prefix, c->memory_swap_max,
prefix, c->memory_limit,
prefix, c->tasks_max,
prefix, cgroup_device_policy_to_string(c->device_policy),
return -errno;
}
+static bool cgroup_context_has_cpu_weight(CGroupContext *c) {
+ return c->cpu_weight != CGROUP_WEIGHT_INVALID ||
+ c->startup_cpu_weight != CGROUP_WEIGHT_INVALID;
+}
+
+static bool cgroup_context_has_cpu_shares(CGroupContext *c) {
+ return c->cpu_shares != CGROUP_CPU_SHARES_INVALID ||
+ c->startup_cpu_shares != CGROUP_CPU_SHARES_INVALID;
+}
+
+static uint64_t cgroup_context_cpu_weight(CGroupContext *c, ManagerState state) {
+ if (IN_SET(state, MANAGER_STARTING, MANAGER_INITIALIZING) &&
+ c->startup_cpu_weight != CGROUP_WEIGHT_INVALID)
+ return c->startup_cpu_weight;
+ else if (c->cpu_weight != CGROUP_WEIGHT_INVALID)
+ return c->cpu_weight;
+ else
+ return CGROUP_WEIGHT_DEFAULT;
+}
+
+static uint64_t cgroup_context_cpu_shares(CGroupContext *c, ManagerState state) {
+ if (IN_SET(state, MANAGER_STARTING, MANAGER_INITIALIZING) &&
+ c->startup_cpu_shares != CGROUP_CPU_SHARES_INVALID)
+ return c->startup_cpu_shares;
+ else if (c->cpu_shares != CGROUP_CPU_SHARES_INVALID)
+ return c->cpu_shares;
+ else
+ return CGROUP_CPU_SHARES_DEFAULT;
+}
+
+static void cgroup_apply_unified_cpu_config(Unit *u, uint64_t weight, uint64_t quota) {
+ char buf[MAX(DECIMAL_STR_MAX(uint64_t) + 1, (DECIMAL_STR_MAX(usec_t) + 1) * 2)];
+ int r;
+
+ xsprintf(buf, "%" PRIu64 "\n", weight);
+ r = cg_set_attribute("cpu", u->cgroup_path, "cpu.weight", buf);
+ if (r < 0)
+ log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
+ "Failed to set cpu.weight: %m");
+
+ if (quota != USEC_INFINITY)
+ xsprintf(buf, USEC_FMT " " USEC_FMT "\n",
+ quota * CGROUP_CPU_QUOTA_PERIOD_USEC / USEC_PER_SEC, CGROUP_CPU_QUOTA_PERIOD_USEC);
+ else
+ xsprintf(buf, "max " USEC_FMT "\n", CGROUP_CPU_QUOTA_PERIOD_USEC);
+
+ r = cg_set_attribute("cpu", u->cgroup_path, "cpu.max", buf);
+
+ if (r < 0)
+ log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
+ "Failed to set cpu.max: %m");
+}
+
+static void cgroup_apply_legacy_cpu_config(Unit *u, uint64_t shares, uint64_t quota) {
+ char buf[MAX(DECIMAL_STR_MAX(uint64_t), DECIMAL_STR_MAX(usec_t)) + 1];
+ int r;
+
+ xsprintf(buf, "%" PRIu64 "\n", shares);
+ r = cg_set_attribute("cpu", u->cgroup_path, "cpu.shares", buf);
+ if (r < 0)
+ log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
+ "Failed to set cpu.shares: %m");
+
+ xsprintf(buf, USEC_FMT "\n", CGROUP_CPU_QUOTA_PERIOD_USEC);
+ r = cg_set_attribute("cpu", u->cgroup_path, "cpu.cfs_period_us", buf);
+ if (r < 0)
+ log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
+ "Failed to set cpu.cfs_period_us: %m");
+
+ if (quota != USEC_INFINITY) {
+ xsprintf(buf, USEC_FMT "\n", quota * CGROUP_CPU_QUOTA_PERIOD_USEC / USEC_PER_SEC);
+ r = cg_set_attribute("cpu", u->cgroup_path, "cpu.cfs_quota_us", buf);
+ } else
+ r = cg_set_attribute("cpu", u->cgroup_path, "cpu.cfs_quota_us", "-1");
+ if (r < 0)
+ log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
+ "Failed to set cpu.cfs_quota_us: %m");
+}
+
+static uint64_t cgroup_cpu_shares_to_weight(uint64_t shares) {
+ return CLAMP(shares * CGROUP_WEIGHT_DEFAULT / CGROUP_CPU_SHARES_DEFAULT,
+ CGROUP_WEIGHT_MIN, CGROUP_WEIGHT_MAX);
+}
+
+static uint64_t cgroup_cpu_weight_to_shares(uint64_t weight) {
+ return CLAMP(weight * CGROUP_CPU_SHARES_DEFAULT / CGROUP_WEIGHT_DEFAULT,
+ CGROUP_CPU_SHARES_MIN, CGROUP_CPU_SHARES_MAX);
+}
+
static bool cgroup_context_has_io_config(CGroupContext *c) {
return c->io_accounting ||
c->io_weight != CGROUP_WEIGHT_INVALID ||
}
static bool cgroup_context_has_unified_memory_config(CGroupContext *c) {
- return c->memory_low > 0 || c->memory_high != CGROUP_LIMIT_MAX || c->memory_max != CGROUP_LIMIT_MAX;
+ return c->memory_low > 0 || c->memory_high != CGROUP_LIMIT_MAX || c->memory_max != CGROUP_LIMIT_MAX || c->memory_swap_max != CGROUP_LIMIT_MAX;
}
static void cgroup_apply_unified_memory_limit(Unit *u, const char *file, uint64_t v) {
* and missing cgroups, i.e. EROFS and ENOENT. */
if ((mask & CGROUP_MASK_CPU) && !is_root) {
- char buf[MAX(DECIMAL_STR_MAX(uint64_t), DECIMAL_STR_MAX(usec_t)) + 1];
+ bool has_weight = cgroup_context_has_cpu_weight(c);
+ bool has_shares = cgroup_context_has_cpu_shares(c);
- sprintf(buf, "%" PRIu64 "\n",
- IN_SET(state, MANAGER_STARTING, MANAGER_INITIALIZING) && c->startup_cpu_shares != CGROUP_CPU_SHARES_INVALID ? c->startup_cpu_shares :
- c->cpu_shares != CGROUP_CPU_SHARES_INVALID ? c->cpu_shares : CGROUP_CPU_SHARES_DEFAULT);
- r = cg_set_attribute("cpu", path, "cpu.shares", buf);
- if (r < 0)
- log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
- "Failed to set cpu.shares: %m");
+ if (cg_all_unified() > 0) {
+ uint64_t weight;
- sprintf(buf, USEC_FMT "\n", CGROUP_CPU_QUOTA_PERIOD_USEC);
- r = cg_set_attribute("cpu", path, "cpu.cfs_period_us", buf);
- if (r < 0)
- log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
- "Failed to set cpu.cfs_period_us: %m");
+ if (has_weight)
+ weight = cgroup_context_cpu_weight(c, state);
+ else if (has_shares) {
+ uint64_t shares = cgroup_context_cpu_shares(c, state);
- if (c->cpu_quota_per_sec_usec != USEC_INFINITY) {
- sprintf(buf, USEC_FMT "\n", c->cpu_quota_per_sec_usec * CGROUP_CPU_QUOTA_PERIOD_USEC / USEC_PER_SEC);
- r = cg_set_attribute("cpu", path, "cpu.cfs_quota_us", buf);
- } else
- r = cg_set_attribute("cpu", path, "cpu.cfs_quota_us", "-1");
- if (r < 0)
- log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
- "Failed to set cpu.cfs_quota_us: %m");
+ weight = cgroup_cpu_shares_to_weight(shares);
+
+ log_cgroup_compat(u, "Applying [Startup]CpuShares %" PRIu64 " as [Startup]CpuWeight %" PRIu64 " on %s",
+ shares, weight, path);
+ } else
+ weight = CGROUP_WEIGHT_DEFAULT;
+
+ cgroup_apply_unified_cpu_config(u, weight, c->cpu_quota_per_sec_usec);
+ } else {
+ uint64_t shares;
+
+ if (has_weight) {
+ uint64_t weight = cgroup_context_cpu_weight(c, state);
+
+ shares = cgroup_cpu_weight_to_shares(weight);
+
+ log_cgroup_compat(u, "Applying [Startup]CpuWeight %" PRIu64 " as [Startup]CpuShares %" PRIu64 " on %s",
+ weight, shares, path);
+ } else if (has_shares)
+ shares = cgroup_context_cpu_shares(c, state);
+ else
+ shares = CGROUP_CPU_SHARES_DEFAULT;
+
+ cgroup_apply_legacy_cpu_config(u, shares, c->cpu_quota_per_sec_usec);
+ }
}
if (mask & CGROUP_MASK_IO) {
char buf[DECIMAL_STR_MAX(uint64_t)+1];
uint64_t weight;
- if (has_blockio)
- weight = cgroup_context_blkio_weight(c, state);
- else if (has_io) {
+ if (has_io) {
uint64_t io_weight = cgroup_context_io_weight(c, state);
weight = cgroup_weight_io_to_blkio(cgroup_context_io_weight(c, state));
log_cgroup_compat(u, "Applying [Startup]IOWeight %" PRIu64 " as [Startup]BlockIOWeight %" PRIu64,
io_weight, weight);
- } else
+ } else if (has_blockio)
+ weight = cgroup_context_blkio_weight(c, state);
+ else
weight = CGROUP_BLKIO_WEIGHT_DEFAULT;
xsprintf(buf, "%" PRIu64 "\n", weight);
log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
"Failed to set blkio.weight: %m");
- if (has_blockio) {
- CGroupBlockIODeviceWeight *w;
-
- /* FIXME: no way to reset this list */
- LIST_FOREACH(device_weights, w, c->blockio_device_weights)
- cgroup_apply_blkio_device_weight(u, w->path, w->weight);
- } else if (has_io) {
+ if (has_io) {
CGroupIODeviceWeight *w;
/* FIXME: no way to reset this list */
cgroup_apply_blkio_device_weight(u, w->path, weight);
}
+ } else if (has_blockio) {
+ CGroupBlockIODeviceWeight *w;
+
+ /* FIXME: no way to reset this list */
+ LIST_FOREACH(device_weights, w, c->blockio_device_weights)
+ cgroup_apply_blkio_device_weight(u, w->path, w->weight);
}
}
/* Apply limits and free ones without config. */
- if (has_blockio) {
- CGroupBlockIODeviceBandwidth *b, *next;
-
- LIST_FOREACH_SAFE(device_bandwidths, b, next, c->blockio_device_bandwidths) {
- if (!cgroup_apply_blkio_device_limit(u, b->path, b->rbps, b->wbps))
- cgroup_context_free_blockio_device_bandwidth(c, b);
- }
- } else if (has_io) {
+ if (has_io) {
CGroupIODeviceLimit *l, *next;
LIST_FOREACH_SAFE(device_limits, l, next, c->io_device_limits) {
if (!cgroup_apply_blkio_device_limit(u, l->path, l->limits[CGROUP_IO_RBPS_MAX], l->limits[CGROUP_IO_WBPS_MAX]))
cgroup_context_free_io_device_limit(c, l);
}
+ } else if (has_blockio) {
+ CGroupBlockIODeviceBandwidth *b, *next;
+
+ LIST_FOREACH_SAFE(device_bandwidths, b, next, c->blockio_device_bandwidths)
+ if (!cgroup_apply_blkio_device_limit(u, b->path, b->rbps, b->wbps))
+ cgroup_context_free_blockio_device_bandwidth(c, b);
}
}
if ((mask & CGROUP_MASK_MEMORY) && !is_root) {
- if (cg_unified() > 0) {
- uint64_t max = c->memory_max;
+ if (cg_all_unified() > 0) {
+ uint64_t max;
+ uint64_t swap_max = CGROUP_LIMIT_MAX;
- if (cgroup_context_has_unified_memory_config(c))
+ if (cgroup_context_has_unified_memory_config(c)) {
max = c->memory_max;
- else {
+ swap_max = c->memory_swap_max;
+ } else {
max = c->memory_limit;
if (max != CGROUP_LIMIT_MAX)
cgroup_apply_unified_memory_limit(u, "memory.low", c->memory_low);
cgroup_apply_unified_memory_limit(u, "memory.high", c->memory_high);
cgroup_apply_unified_memory_limit(u, "memory.max", max);
+ cgroup_apply_unified_memory_limit(u, "memory.swap.max", swap_max);
} else {
char buf[DECIMAL_STR_MAX(uint64_t) + 1];
- uint64_t val = c->memory_limit;
+ uint64_t val;
- if (val == CGROUP_LIMIT_MAX) {
+ if (cgroup_context_has_unified_memory_config(c)) {
val = c->memory_max;
-
- if (val != CGROUP_LIMIT_MAX)
- log_cgroup_compat(u, "Applying MemoryMax %" PRIi64 " as MemoryLimit", c->memory_max);
- }
+ log_cgroup_compat(u, "Applying MemoryMax %" PRIi64 " as MemoryLimit", val);
+ } else
+ val = c->memory_limit;
if (val == CGROUP_LIMIT_MAX)
strncpy(buf, "-1\n", sizeof(buf));
if ((mask & CGROUP_MASK_PIDS) && !is_root) {
- if (c->tasks_max != (uint64_t) -1) {
+ if (c->tasks_max != CGROUP_LIMIT_MAX) {
char buf[DECIMAL_STR_MAX(uint64_t) + 2];
sprintf(buf, "%" PRIu64 "\n", c->tasks_max);
/* Figure out which controllers we need */
if (c->cpu_accounting ||
- c->cpu_shares != CGROUP_CPU_SHARES_INVALID ||
- c->startup_cpu_shares != CGROUP_CPU_SHARES_INVALID ||
+ cgroup_context_has_cpu_weight(c) ||
+ cgroup_context_has_cpu_shares(c) ||
c->cpu_quota_per_sec_usec != USEC_INFINITY)
mask |= CGROUP_MASK_CPUACCT | CGROUP_MASK_CPU;
e = unit_get_exec_context(u);
if (!e ||
exec_context_maintains_privileges(e) ||
- cg_unified() > 0)
+ cg_all_unified() > 0)
return _CGROUP_MASK_ALL;
}
return 0;
/* Only applies to the unified hierarchy */
- r = cg_unified();
+ r = cg_unified(SYSTEMD_CGROUP_CONTROLLER);
if (r < 0)
return log_unit_error_errno(u, r, "Failed detect whether the unified hierarchy is used: %m");
if (r == 0)
return 0;
}
+static void cgroup_xattr_apply(Unit *u) {
+ char ids[SD_ID128_STRING_MAX];
+ int r;
+
+ assert(u);
+
+ if (!MANAGER_IS_SYSTEM(u->manager))
+ return;
+
+ if (sd_id128_is_null(u->invocation_id))
+ return;
+
+ r = cg_set_xattr(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path,
+ "trusted.invocation_id",
+ sd_id128_to_string(u->invocation_id, ids), 32,
+ 0);
+ if (r < 0)
+ log_unit_warning_errno(u, r, "Failed to set invocation ID on control group %s, ignoring: %m", u->cgroup_path);
+}
+
static bool unit_has_mask_realized(Unit *u, CGroupMask target_mask, CGroupMask enable_mask) {
assert(u);
/* Finally, apply the necessary attributes. */
cgroup_context_apply(u, target_mask, state);
+ cgroup_xattr_apply(u);
return 0;
}
if (!u->cgroup_path)
return;
+ (void) unit_get_cpu_usage(u, NULL); /* Cache the last CPU usage value before we destroy the cgroup */
+
is_root_slice = unit_has_name(u, SPECIAL_ROOT_SLICE);
r = cg_trim_everywhere(u->manager->cgroup_supported, u->cgroup_path, !is_root_slice);
if (!u->cgroup_path)
return -ENOENT;
- if (cg_unified() > 0) /* On unified we can use proper notifications */
+ if (cg_unified(SYSTEMD_CGROUP_CONTROLLER) > 0) /* On unified we can use proper notifications */
return 0;
return unit_watch_pids_in_path(u, u->cgroup_path);
int manager_setup_cgroup(Manager *m) {
_cleanup_free_ char *path = NULL;
CGroupController c;
- int r, unified;
+ int r, all_unified, systemd_unified;
char *e;
assert(m);
if (r < 0)
return log_error_errno(r, "Cannot find cgroup mount point: %m");
- unified = cg_unified();
- if (unified < 0)
- return log_error_errno(r, "Couldn't determine if we are running in the unified hierarchy: %m");
- if (unified > 0)
+ all_unified = cg_all_unified();
+ systemd_unified = cg_unified(SYSTEMD_CGROUP_CONTROLLER);
+
+ if (all_unified < 0 || systemd_unified < 0)
+ return log_error_errno(all_unified < 0 ? all_unified : systemd_unified,
+ "Couldn't determine if we are running in the unified hierarchy: %m");
+
+ if (all_unified > 0)
log_debug("Unified cgroup hierarchy is located at %s.", path);
+ else if (systemd_unified > 0)
+ log_debug("Unified cgroup hierarchy is located at %s. Controllers are on legacy hierarchies.", path);
else
log_debug("Using cgroup controller " SYSTEMD_CGROUP_CONTROLLER ". File system hierarchy is at %s.", path);
const char *scope_path;
/* 3. Install agent */
- if (unified) {
+ if (systemd_unified) {
/* In the unified hierarchy we can get
* cgroup empty notifications via inotify. */
return log_error_errno(errno, "Failed to open pin file: %m");
/* 6. Always enable hierarchical support if it exists... */
- if (!unified)
+ if (!all_unified)
(void) cg_set_attribute("memory", "/", "memory.use_hierarchy", "1");
}
return 0;
}
#endif // 0
-
#if 0 /// UNNEEDED by elogind
int unit_get_memory_current(Unit *u, uint64_t *ret) {
_cleanup_free_ char *v = NULL;
if ((u->cgroup_realized_mask & CGROUP_MASK_MEMORY) == 0)
return -ENODATA;
- if (cg_unified() <= 0)
+ if (cg_all_unified() <= 0)
r = cg_get_attribute("memory", u->cgroup_path, "memory.usage_in_bytes", &v);
else
r = cg_get_attribute("memory", u->cgroup_path, "memory.current", &v);
if (!u->cgroup_path)
return -ENODATA;
- if ((u->cgroup_realized_mask & CGROUP_MASK_CPUACCT) == 0)
- return -ENODATA;
+ if (cg_all_unified() > 0) {
+ const char *keys[] = { "usage_usec", NULL };
+ _cleanup_free_ char *val = NULL;
+ uint64_t us;
- r = cg_get_attribute("cpuacct", u->cgroup_path, "cpuacct.usage", &v);
- if (r == -ENOENT)
- return -ENODATA;
- if (r < 0)
- return r;
+ if ((u->cgroup_realized_mask & CGROUP_MASK_CPU) == 0)
+ return -ENODATA;
- r = safe_atou64(v, &ns);
- if (r < 0)
- return r;
+ r = cg_get_keyed_attribute("cpu", u->cgroup_path, "cpu.stat", keys, &val);
+ if (r < 0)
+ return r;
+
+ r = safe_atou64(val, &us);
+ if (r < 0)
+ return r;
+
+ ns = us * NSEC_PER_USEC;
+ } else {
+ if ((u->cgroup_realized_mask & CGROUP_MASK_CPUACCT) == 0)
+ return -ENODATA;
+
+ r = cg_get_attribute("cpuacct", u->cgroup_path, "cpuacct.usage", &v);
+ if (r == -ENOENT)
+ return -ENODATA;
+ if (r < 0)
+ return r;
+
+ r = safe_atou64(v, &ns);
+ if (r < 0)
+ return r;
+ }
*ret = ns;
return 0;
nsec_t ns;
int r;
+ assert(u);
+
+ /* Retrieve the current CPU usage counter. This will subtract the CPU counter taken when the unit was
+ * started. If the cgroup has been removed already, returns the last cached value. To cache the value, simply
+ * call this function with a NULL return value. */
+
r = unit_get_cpu_usage_raw(u, &ns);
+ if (r == -ENODATA && u->cpu_usage_last != NSEC_INFINITY) {
+ /* If we can't get the CPU usage anymore (because the cgroup was already removed, for example), use our
+ * cached value. */
+
+ if (ret)
+ *ret = u->cpu_usage_last;
+ return 0;
+ }
if (r < 0)
return r;
- if (ns > u->cpuacct_usage_base)
- ns -= u->cpuacct_usage_base;
+ if (ns > u->cpu_usage_base)
+ ns -= u->cpu_usage_base;
else
ns = 0;
- *ret = ns;
+ u->cpu_usage_last = ns;
+ if (ret)
+ *ret = ns;
+
return 0;
}
assert(u);
+ u->cpu_usage_last = NSEC_INFINITY;
+
r = unit_get_cpu_usage_raw(u, &ns);
if (r < 0) {
- u->cpuacct_usage_base = 0;
+ u->cpu_usage_base = 0;
return r;
}
- u->cpuacct_usage_base = ns;
+ u->cpu_usage_base = ns;
return 0;
}
bool tasks_accounting;
/* For unified hierarchy */
+ uint64_t cpu_weight;
+ uint64_t startup_cpu_weight;
+ usec_t cpu_quota_per_sec_usec;
+
uint64_t io_weight;
uint64_t startup_io_weight;
LIST_HEAD(CGroupIODeviceWeight, io_device_weights);
uint64_t memory_low;
uint64_t memory_high;
uint64_t memory_max;
+ uint64_t memory_swap_max;
/* For legacy hierarchies */
uint64_t cpu_shares;
uint64_t startup_cpu_shares;
- usec_t cpu_quota_per_sec_usec;
uint64_t blockio_weight;
uint64_t startup_blockio_weight;
{ "tmpfs", "/sys/fs/cgroup", "tmpfs", "mode=755", MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME,
cg_is_legacy_wanted, MNT_FATAL|MNT_IN_CONTAINER },
#if 0 /// UNNEEDED by elogind
+ { "cgroup", "/sys/fs/cgroup/systemd", "cgroup2", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV,
+ cg_is_unified_systemd_controller_wanted, MNT_IN_CONTAINER },
{ "cgroup", "/sys/fs/cgroup/systemd", "cgroup", "none,name=systemd,xattr", MS_NOSUID|MS_NOEXEC|MS_NODEV,
- cg_is_legacy_wanted, MNT_IN_CONTAINER },
+ cg_is_legacy_systemd_controller_wanted, MNT_IN_CONTAINER },
{ "cgroup", "/sys/fs/cgroup/systemd", "cgroup", "none,name=systemd", MS_NOSUID|MS_NOEXEC|MS_NODEV,
- cg_is_legacy_wanted, MNT_FATAL|MNT_IN_CONTAINER },
#else
{ "cgroup", "/sys/fs/cgroup/elogind", "cgroup", "none,name=elogind,xattr", MS_NOSUID|MS_NOEXEC|MS_NODEV,
cg_is_legacy_wanted, MNT_IN_CONTAINER },
global:
sd_event_get_iteration;
} LIBSYSTEMD_226;
+
+LIBSYSTEMD_232 {
+global:
+ /* sd_bus_track_set_recursive; */
+ /* sd_bus_track_get_recursive; */
+ /* sd_bus_track_count_name; */
+ /* sd_bus_track_count_sender; */
+ sd_bus_set_exit_on_disconnect;
+ /* sd_bus_get_exit_on_disconnect; */
+ sd_id128_get_invocation;
+} LIBSYSTEMD_231;
#if 0 /// UNNEEDED by elogind
SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_UNIT, ENOENT),
SD_BUS_ERROR_MAP(BUS_ERROR_NO_UNIT_FOR_PID, ESRCH),
+ SD_BUS_ERROR_MAP(BUS_ERROR_NO_UNIT_FOR_INVOCATION_ID, ENOENT),
SD_BUS_ERROR_MAP(BUS_ERROR_UNIT_EXISTS, EEXIST),
SD_BUS_ERROR_MAP(BUS_ERROR_LOAD_FAILED, EIO),
SD_BUS_ERROR_MAP(BUS_ERROR_JOB_FAILED, EREMOTEIO),
SD_BUS_ERROR_MAP(BUS_ERROR_NO_ISOLATION, EPERM),
SD_BUS_ERROR_MAP(BUS_ERROR_SHUTTING_DOWN, ECANCELED),
SD_BUS_ERROR_MAP(BUS_ERROR_SCOPE_NOT_RUNNING, EHOSTDOWN),
+ SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_DYNAMIC_USER, ESRCH),
+ SD_BUS_ERROR_MAP(BUS_ERROR_NOT_REFERENCED, EUNATCH),
SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_MACHINE, ENXIO),
SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_IMAGE, ENOENT),
SD_BUS_ERROR_MAP(BUS_ERROR_NO_MACHINE_FOR_PID, ENXIO),
SD_BUS_ERROR_MAP(BUS_ERROR_MACHINE_EXISTS, EEXIST),
SD_BUS_ERROR_MAP(BUS_ERROR_NO_PRIVATE_NETWORKING, ENOSYS),
+ SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_USER_MAPPING, ENXIO),
+ SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_GROUP_MAPPING, ENXIO),
#endif // 0
SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_SESSION, ENXIO),
SD_BUS_ERROR_MAP(BUS_ERROR_DEVICE_NOT_TAKEN, EINVAL),
SD_BUS_ERROR_MAP(BUS_ERROR_OPERATION_IN_PROGRESS, EINPROGRESS),
SD_BUS_ERROR_MAP(BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED, EOPNOTSUPP),
+ SD_BUS_ERROR_MAP(BUS_ERROR_SESSION_BUSY, EBUSY),
#if 0 /// UNNEEDED by elogind
SD_BUS_ERROR_MAP(BUS_ERROR_AUTOMATIC_TIME_SYNC_ENABLED, EALREADY),
SD_BUS_ERROR_MAP(BUS_ERROR_LINK_BUSY, EBUSY),
SD_BUS_ERROR_MAP(BUS_ERROR_NETWORK_DOWN, ENETDOWN),
+ SD_BUS_ERROR_MAP(_BUS_ERROR_DNS "FORMERR", EBADMSG),
+ SD_BUS_ERROR_MAP(_BUS_ERROR_DNS "SERVFAIL", EHOSTDOWN),
+ SD_BUS_ERROR_MAP(_BUS_ERROR_DNS "NXDOMAIN", ENXIO),
+ SD_BUS_ERROR_MAP(_BUS_ERROR_DNS "NOTIMP", ENOSYS),
+ SD_BUS_ERROR_MAP(_BUS_ERROR_DNS "REFUSED", EACCES),
+ SD_BUS_ERROR_MAP(_BUS_ERROR_DNS "YXDOMAIN", EEXIST),
+ SD_BUS_ERROR_MAP(_BUS_ERROR_DNS "YRRSET", EEXIST),
+ SD_BUS_ERROR_MAP(_BUS_ERROR_DNS "NXRRSET", ENOENT),
+ SD_BUS_ERROR_MAP(_BUS_ERROR_DNS "NOTAUTH", EACCES),
+ SD_BUS_ERROR_MAP(_BUS_ERROR_DNS "NOTZONE", EREMOTE),
+ SD_BUS_ERROR_MAP(_BUS_ERROR_DNS "BADVERS", EBADMSG),
+ SD_BUS_ERROR_MAP(_BUS_ERROR_DNS "BADKEY", EKEYREJECTED),
+ SD_BUS_ERROR_MAP(_BUS_ERROR_DNS "BADTIME", EBADMSG),
+ SD_BUS_ERROR_MAP(_BUS_ERROR_DNS "BADMODE", EBADMSG),
+ SD_BUS_ERROR_MAP(_BUS_ERROR_DNS "BADNAME", EBADMSG),
+ SD_BUS_ERROR_MAP(_BUS_ERROR_DNS "BADALG", EBADMSG),
+ SD_BUS_ERROR_MAP(_BUS_ERROR_DNS "BADTRUNC", EBADMSG),
+ SD_BUS_ERROR_MAP(_BUS_ERROR_DNS "BADCOOKIE", EBADR),
+
SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_TRANSFER, ENXIO),
SD_BUS_ERROR_MAP(BUS_ERROR_TRANSFER_IN_PROGRESS, EBUSY),
#endif // 0
SD_BUS_ERROR_MAP_END
};
-/* GCC maps this magically to the beginning and end of the BUS_ERROR_MAP section.
- * Hide them; for currently unknown reasons they get exported to the shared libries
- * even without being listed in the sym file. */
-extern const sd_bus_error_map __start_BUS_ERROR_MAP[] _hidden_;
-extern const sd_bus_error_map __stop_BUS_ERROR_MAP[] _hidden_;
+/* GCC maps this magically to the beginning and end of the BUS_ERROR_MAP section */
+extern const sd_bus_error_map __start_BUS_ERROR_MAP[];
+extern const sd_bus_error_map __stop_BUS_ERROR_MAP[];
/* Additional maps registered with sd_bus_error_add_map() are in this
* NULL terminated array */
bool is_system:1;
bool is_user:1;
bool allow_interactive_authorization:1;
+ bool exit_on_disconnect:1;
+ bool exited:1;
+ bool exit_triggered:1;
int use_memfd;
sd_bus_track *track_queue;
LIST_HEAD(sd_bus_slot, slots);
+ LIST_HEAD(sd_bus_track, tracks);
};
#define BUS_DEFAULT_TIMEOUT ((usec_t) (25 * USEC_PER_SEC))
-#define BUS_WQUEUE_MAX 1024
-#define BUS_RQUEUE_MAX 64*1024
+#define BUS_WQUEUE_MAX (192*1024)
+#define BUS_RQUEUE_MAX (192*1024)
#define BUS_MESSAGE_SIZE_MAX (64*1024*1024)
#define BUS_AUTH_SIZE_MAX (64*1024)
bus_slot_disconnect(slot);
free(slot->description);
- free(slot);
-
- return NULL;
+ return mfree(slot);
}
#if 0 /// UNNEEDED by elogind
#include "bus-track.h"
#include "bus-util.h"
+struct track_item {
+ unsigned n_ref;
+ char *name;
+ sd_bus_slot *slot;
+};
+
struct sd_bus_track {
unsigned n_ref;
+ unsigned n_adding; /* are we in the process of adding a new name? */
sd_bus *bus;
sd_bus_track_handler_t handler;
void *userdata;
Hashmap *names;
LIST_FIELDS(sd_bus_track, queue);
Iterator iterator;
- bool in_queue;
- bool modified;
+ bool in_list:1; /* In bus->tracks? */
+ bool in_queue:1; /* In bus->track_queue? */
+ bool modified:1;
+ bool recursive:1;
+
+ LIST_FIELDS(sd_bus_track, tracks);
};
#define MATCH_PREFIX \
_x; \
})
+static struct track_item* track_item_free(struct track_item *i) {
+
+ if (!i)
+ return NULL;
+
+ sd_bus_slot_unref(i->slot);
+ free(i->name);
+ return mfree(i);
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(struct track_item*, track_item_free);
+
static void bus_track_add_to_queue(sd_bus_track *track) {
assert(track);
+ /* Adds the bus track object to the queue of objects we should dispatch next, subject to a number of
+ * conditions. */
+
+ /* Already in the queue? */
if (track->in_queue)
return;
+ /* if we are currently in the process of adding a new name, then let's not enqueue this just yet, let's wait
+ * until the addition is complete. */
+ if (track->n_adding > 0)
+ return;
+
+ /* still referenced? */
+ if (hashmap_size(track->names) > 0)
+ return;
+
+ /* Nothing to call? */
if (!track->handler)
return;
+ /* Already closed? */
+ if (!track->in_list)
+ return;
+
LIST_PREPEND(queue, track->bus->track_queue, track);
track->in_queue = true;
}
track->in_queue = false;
}
+static int bus_track_remove_name_fully(sd_bus_track *track, const char *name) {
+ struct track_item *i;
+
+ assert(track);
+ assert(name);
+
+ i = hashmap_remove(track->names, name);
+ if (!i)
+ return 0;
+
+ track_item_free(i);
+
+ bus_track_add_to_queue(track);
+
+ track->modified = true;
+ return 1;
+}
+
_public_ int sd_bus_track_new(
sd_bus *bus,
sd_bus_track **track,
t->userdata = userdata;
t->bus = sd_bus_ref(bus);
+ LIST_PREPEND(tracks, bus->tracks, t);
+ t->in_list = true;
+
bus_track_add_to_queue(t);
*track = t;
}
_public_ sd_bus_track* sd_bus_track_unref(sd_bus_track *track) {
- const char *n;
+ struct track_item *i;
if (!track)
return NULL;
return NULL;
}
- while ((n = hashmap_first_key(track->names)))
- sd_bus_track_remove_name(track, n);
+ while ((i = hashmap_steal_first(track->names)))
+ track_item_free(i);
+
+ if (track->in_list)
+ LIST_REMOVE(tracks, track->bus->tracks, track);
bus_track_remove_from_queue(track);
hashmap_free(track->names);
sd_bus_unref(track->bus);
- free(track);
-
- return NULL;
+ return mfree(track);
}
static int on_name_owner_changed(sd_bus_message *message, void *userdata, sd_bus_error *error) {
if (r < 0)
return 0;
- sd_bus_track_remove_name(track, name);
+ bus_track_remove_name_fully(track, name);
return 0;
}
_public_ int sd_bus_track_add_name(sd_bus_track *track, const char *name) {
- _cleanup_(sd_bus_slot_unrefp) sd_bus_slot *slot = NULL;
- _cleanup_free_ char *n = NULL;
+ _cleanup_(track_item_freep) struct track_item *n = NULL;
+ struct track_item *i;
const char *match;
int r;
assert_return(track, -EINVAL);
assert_return(service_name_is_valid(name), -EINVAL);
+ i = hashmap_get(track->names, name);
+ if (i) {
+ if (track->recursive) {
+ unsigned k = track->n_ref + 1;
+
+ if (k < track->n_ref) /* Check for overflow */
+ return -EOVERFLOW;
+
+ track->n_ref = k;
+ }
+
+ bus_track_remove_from_queue(track);
+ return 0;
+ }
+
r = hashmap_ensure_allocated(&track->names, &string_hash_ops);
if (r < 0)
return r;
- n = strdup(name);
+ n = new0(struct track_item, 1);
if (!n)
return -ENOMEM;
+ n->name = strdup(name);
+ if (!n->name)
+ return -ENOMEM;
/* First, subscribe to this name */
- match = MATCH_FOR_NAME(n);
- r = sd_bus_add_match(track->bus, &slot, match, on_name_owner_changed, track);
- if (r < 0)
+ match = MATCH_FOR_NAME(name);
+
+ bus_track_remove_from_queue(track); /* don't dispatch this while we work in it */
+
+ track->n_adding++; /* make sure we aren't dispatched while we synchronously add this match */
+ r = sd_bus_add_match(track->bus, &n->slot, match, on_name_owner_changed, track);
+ track->n_adding--;
+ if (r < 0) {
+ bus_track_add_to_queue(track);
return r;
+ }
- r = hashmap_put(track->names, n, slot);
- if (r == -EEXIST)
- return 0;
- if (r < 0)
+ r = hashmap_put(track->names, n->name, n);
+ if (r < 0) {
+ bus_track_add_to_queue(track);
return r;
+ }
- /* Second, check if it is currently existing, or maybe
- * doesn't, or maybe disappeared already. */
- r = sd_bus_get_name_creds(track->bus, n, 0, NULL);
+ /* Second, check if it is currently existing, or maybe doesn't, or maybe disappeared already. */
+ track->n_adding++; /* again, make sure this isn't dispatch while we are working in it */
+ r = sd_bus_get_name_creds(track->bus, name, 0, NULL);
+ track->n_adding--;
if (r < 0) {
- hashmap_remove(track->names, n);
+ hashmap_remove(track->names, name);
+ bus_track_add_to_queue(track);
return r;
}
+ n->n_ref = 1;
n = NULL;
- slot = NULL;
bus_track_remove_from_queue(track);
track->modified = true;
}
_public_ int sd_bus_track_remove_name(sd_bus_track *track, const char *name) {
- _cleanup_(sd_bus_slot_unrefp) sd_bus_slot *slot = NULL;
- _cleanup_free_ char *n = NULL;
+ struct track_item *i;
assert_return(name, -EINVAL);
- if (!track)
+ if (!track) /* Treat a NULL track object as an empty track object */
return 0;
- slot = hashmap_remove2(track->names, (char*) name, (void**) &n);
- if (!slot)
- return 0;
+ if (!track->recursive)
+ return bus_track_remove_name_fully(track, name);
- if (hashmap_isempty(track->names))
- bus_track_add_to_queue(track);
+ i = hashmap_get(track->names, name);
+ if (!i)
+ return -EUNATCH;
+ if (i->n_ref <= 0)
+ return -EUNATCH;
- track->modified = true;
+ i->n_ref--;
+
+ if (i->n_ref <= 0)
+ return bus_track_remove_name_fully(track, name);
return 1;
}
_public_ unsigned sd_bus_track_count(sd_bus_track *track) {
- if (!track)
+
+ if (!track) /* Let's consider a NULL object equivalent to an empty object */
return 0;
+ /* This signature really should have returned an int, so that we can propagate errors. But well, ... Also, note
+ * that this returns the number of names being watched, and multiple references to the same name are not
+ * counted. */
+
return hashmap_size(track->names);
}
_public_ const char* sd_bus_track_contains(sd_bus_track *track, const char *name) {
- assert_return(track, NULL);
assert_return(name, NULL);
+ if (!track) /* Let's consider a NULL object equivalent to an empty object */
+ return NULL;
+
return hashmap_get(track->names, (void*) name) ? name : NULL;
}
assert_return(track, -EINVAL);
assert_return(m, -EINVAL);
+ if (sd_bus_message_get_bus(m) != track->bus)
+ return -EINVAL;
+
sender = sd_bus_message_get_sender(m);
if (!sender)
return -EINVAL;
_public_ int sd_bus_track_remove_sender(sd_bus_track *track, sd_bus_message *m) {
const char *sender;
- assert_return(track, -EINVAL);
assert_return(m, -EINVAL);
+ if (!track) /* Treat a NULL track object as an empty track object */
+ return 0;
+
+ if (sd_bus_message_get_bus(m) != track->bus)
+ return -EINVAL;
+
sender = sd_bus_message_get_sender(m);
if (!sender)
return -EINVAL;
int r;
assert(track);
- assert(track->in_queue);
assert(track->handler);
bus_track_remove_from_queue(track);
sd_bus_track_unref(track);
}
+void bus_track_close(sd_bus_track *track) {
+ struct track_item *i;
+
+ assert(track);
+
+ /* Called whenever our bus connected is closed. If so, and our track object is non-empty, dispatch it
+ * immediately, as we are closing now, but first flush out all names. */
+
+ if (!track->in_list)
+ return; /* We already closed this one, don't close it again. */
+
+ /* Remember that this one is closed now */
+ LIST_REMOVE(tracks, track->bus->tracks, track);
+ track->in_list = false;
+
+ /* If there's no name in this one anyway, we don't have to dispatch */
+ if (hashmap_isempty(track->names))
+ return;
+
+ /* Let's flush out all names */
+ while ((i = hashmap_steal_first(track->names)))
+ track_item_free(i);
+
+ /* Invoke handler */
+ if (track->handler)
+ bus_track_dispatch(track);
+}
+
#if 0 /// UNNEEDED by elogind
_public_ void *sd_bus_track_get_userdata(sd_bus_track *track) {
assert_return(track, NULL);
return ret;
}
+
+_public_ int sd_bus_track_set_recursive(sd_bus_track *track, int b) {
+ assert_return(track, -EINVAL);
+
+ if (track->recursive == !!b)
+ return 0;
+
+ if (!hashmap_isempty(track->names))
+ return -EBUSY;
+
+ track->recursive = b;
+ return 0;
+}
+
+_public_ int sd_bus_track_get_recursive(sd_bus_track *track) {
+ assert_return(track, -EINVAL);
+
+ return track->recursive;
+}
+
+_public_ int sd_bus_track_count_sender(sd_bus_track *track, sd_bus_message *m) {
+ const char *sender;
+
+ assert_return(m, -EINVAL);
+
+ if (!track) /* Let's consider a NULL object equivalent to an empty object */
+ return 0;
+
+ if (sd_bus_message_get_bus(m) != track->bus)
+ return -EINVAL;
+
+ sender = sd_bus_message_get_sender(m);
+ if (!sender)
+ return -EINVAL;
+
+ return sd_bus_track_count_name(track, sender);
+}
+
+_public_ int sd_bus_track_count_name(sd_bus_track *track, const char *name) {
+ struct track_item *i;
+
+ assert_return(service_name_is_valid(name), -EINVAL);
+
+ if (!track) /* Let's consider a NULL object equivalent to an empty object */
+ return 0;
+
+ i = hashmap_get(track->names, name);
+ if (!i)
+ return 0;
+
+ return i->n_ref;
+}
#endif // 0
***/
void bus_track_dispatch(sd_bus_track *track);
+void bus_track_close(sd_bus_track *track);
assert(b);
assert(!b->track_queue);
+ assert(!b->tracks);
b->state = BUS_CLOSED;
return r;
}
-static int process_closing(sd_bus *bus, sd_bus_message **ret) {
+static int bus_exit_now(sd_bus *bus) {
+ assert(bus);
+
+ /* Exit due to close, if this is requested. If this is bus object is attached to an event source, invokes
+ * sd_event_exit(), otherwise invokes libc exit(). */
+
+ if (bus->exited) /* did we already exit? */
+ return 0;
+ if (!bus->exit_triggered) /* was the exit condition triggered? */
+ return 0;
+ if (!bus->exit_on_disconnect) /* Shall we actually exit on disconnection? */
+ return 0;
+
+ bus->exited = true; /* never exit more than once */
+
+ log_debug("Bus connection disconnected, exiting.");
+
+ if (bus->event)
+ return sd_event_exit(bus->event, EXIT_FAILURE);
+ else
+ exit(EXIT_FAILURE);
+
+ assert_not_reached("exit() didn't exit?");
+}
+
+static int process_closing_reply_callback(sd_bus *bus, struct reply_callback *c) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
- struct reply_callback *c;
+ sd_bus_slot *slot;
int r;
assert(bus);
- assert(bus->state == BUS_CLOSING);
+ assert(c);
- c = ordered_hashmap_first(bus->reply_callbacks);
- if (c) {
- _cleanup_(sd_bus_error_free) sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
- sd_bus_slot *slot;
+ r = bus_message_new_synthetic_error(
+ bus,
+ c->cookie,
+ &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_NO_REPLY, "Connection terminated"),
+ &m);
+ if (r < 0)
+ return r;
- /* First, fail all outstanding method calls */
- r = bus_message_new_synthetic_error(
- bus,
- c->cookie,
- &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_NO_REPLY, "Connection terminated"),
- &m);
- if (r < 0)
- return r;
+ r = bus_seal_synthetic_message(bus, m);
+ if (r < 0)
+ return r;
- r = bus_seal_synthetic_message(bus, m);
- if (r < 0)
- return r;
+ if (c->timeout != 0) {
+ prioq_remove(bus->reply_callbacks_prioq, c, &c->prioq_idx);
+ c->timeout = 0;
+ }
- if (c->timeout != 0) {
- prioq_remove(bus->reply_callbacks_prioq, c, &c->prioq_idx);
- c->timeout = 0;
- }
+ ordered_hashmap_remove(bus->reply_callbacks, &c->cookie);
+ c->cookie = 0;
+
+ slot = container_of(c, sd_bus_slot, reply_callback);
- ordered_hashmap_remove(bus->reply_callbacks, &c->cookie);
- c->cookie = 0;
+ bus->iteration_counter++;
- slot = container_of(c, sd_bus_slot, reply_callback);
+ bus->current_message = m;
+ bus->current_slot = sd_bus_slot_ref(slot);
+ bus->current_handler = c->callback;
+ bus->current_userdata = slot->userdata;
+ r = c->callback(m, slot->userdata, &error_buffer);
+ bus->current_userdata = NULL;
+ bus->current_handler = NULL;
+ bus->current_slot = NULL;
+ bus->current_message = NULL;
- bus->iteration_counter++;
+ if (slot->floating) {
+ bus_slot_disconnect(slot);
+ sd_bus_slot_unref(slot);
+ }
- bus->current_message = m;
- bus->current_slot = sd_bus_slot_ref(slot);
- bus->current_handler = c->callback;
- bus->current_userdata = slot->userdata;
- r = c->callback(m, slot->userdata, &error_buffer);
- bus->current_userdata = NULL;
- bus->current_handler = NULL;
- bus->current_slot = NULL;
- bus->current_message = NULL;
+ sd_bus_slot_unref(slot);
- if (slot->floating) {
- bus_slot_disconnect(slot);
- sd_bus_slot_unref(slot);
- }
+ return bus_maybe_reply_error(m, r, &error_buffer);
+}
- sd_bus_slot_unref(slot);
+static int process_closing(sd_bus *bus, sd_bus_message **ret) {
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
+ struct reply_callback *c;
+ int r;
+
+ assert(bus);
+ assert(bus->state == BUS_CLOSING);
- return bus_maybe_reply_error(m, r, &error_buffer);
+ /* First, fail all outstanding method calls */
+ c = ordered_hashmap_first(bus->reply_callbacks);
+ if (c)
+ return process_closing_reply_callback(bus, c);
+
+ /* Then, fake-drop all remaining bus tracking references */
+ if (bus->tracks) {
+ bus_track_close(bus->tracks);
+ return 1;
}
/* Then, synthesize a Disconnected message */
if (r != 0)
goto finish;
+ /* Nothing else to do, exit now, if the condition holds */
+ bus->exit_triggered = true;
+ (void) bus_exit_now(bus);
+
if (ret) {
*ret = m;
m = NULL;
flush_close(default_system_bus);
}
#endif // 0
+
+_public_ int sd_bus_set_exit_on_disconnect(sd_bus *bus, int b) {
+ assert_return(bus, -EINVAL);
+
+ /* Turns on exit-on-disconnect, and triggers it immediately if the bus connection was already
+ * disconnected. Note that this is triggered exclusively on disconnections triggered by the server side, never
+ * from the client side. */
+ bus->exit_on_disconnect = b;
+
+ /* If the exit condition was triggered already, exit immediately. */
+ return bus_exit_now(bus);
+}
+
+#if 0 /// UNNEEDED by elogind
+_public_ int sd_bus_get_exit_on_disconnect(sd_bus *bus) {
+ assert_return(bus, -EINVAL);
+
+ return bus->exit_on_disconnect;
+}
+#endif // 0
/* If all the mask is all-zero we can get rid of the structure */
hashmap_remove(e->signal_data, &d->priority);
+ assert(!d->current);
safe_close(d->fd);
free(d);
return;
return id128_read_fd(fd, f, ret);
}
+#if 0 /// UNNEEDED by elogind
int id128_write_fd(int fd, Id128Format f, sd_id128_t id, bool do_sync) {
char buffer[36 + 2];
size_t sz;
return id128_write_fd(fd, f, id, do_sync);
}
+
+void id128_hash_func(const void *p, struct siphash *state) {
+ siphash24_compress(p, 16, state);
+}
+
+int id128_compare_func(const void *a, const void *b) {
+ return memcmp(a, b, 16);
+}
+
+const struct hash_ops id128_hash_ops = {
+ .hash = id128_hash_func,
+ .compare = id128_compare_func,
+};
+#endif // 0
#include <stdbool.h>
#include "sd-id128.h"
+
+#include "hash-funcs.h"
#include "macro.h"
char *id128_to_uuid_string(sd_id128_t id, char s[37]);
int id128_read_fd(int fd, Id128Format f, sd_id128_t *ret);
int id128_read(const char *p, Id128Format f, sd_id128_t *ret);
+#if 0 /// UNNEEDED by elogind
int id128_write_fd(int fd, Id128Format f, sd_id128_t id, bool do_sync);
int id128_write(const char *p, Id128Format f, sd_id128_t id, bool do_sync);
+
+void id128_hash_func(const void *p, struct siphash *state);
+int id128_compare_func(const void *a, const void *b) _pure_;
+extern const struct hash_ops id128_hash_ops;
+#endif // 0
return 0;
}
+_public_ int sd_id128_get_invocation(sd_id128_t *ret) {
+ static thread_local sd_id128_t saved_invocation_id = {};
+ int r;
+
+ assert_return(ret, -EINVAL);
+
+ if (sd_id128_is_null(saved_invocation_id)) {
+ const char *e;
+
+ e = secure_getenv("INVOCATION_ID");
+ if (!e)
+ return -ENXIO;
+
+ r = sd_id128_from_string(e, &saved_invocation_id);
+ if (r < 0)
+ return r;
+ }
+
+ *ret = saved_invocation_id;
+ return 0;
+}
+
static sd_id128_t make_v4_uuid(sd_id128_t id) {
/* Stolen from generate_random_uuid() of drivers/char/random.c
* in the kernel sources */
#
# Used by systemd --user instances.
-account include system-auth
+account required pam_unix.so
m4_ifdef(`HAVE_SELINUX',
session required pam_selinux.so close
session required pam_selinux.so nottys open
)m4_dnl
session required pam_loginuid.so
-session include system-auth
+session optional pam_elogind.so
if (!MANAGER_IS_SYSTEM(m))
return 0;
- if (cg_unified() > 0) /* We don't need this anymore on the unified hierarchy */
+ if (cg_unified(SYSTEMD_CGROUP_CONTROLLER) > 0) /* We don't need this anymore on the unified hierarchy */
return 0;
if (m->cgroups_agent_fd < 0) {
}
#endif // 0
+static int get_session_path(sd_bus *bus, const char *session_id, sd_bus_error *error, char **path) {
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ int r;
+ char *ans;
+
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.login1",
+ "/org/freedesktop/login1",
+ "org.freedesktop.login1.Manager",
+ "GetSession",
+ error, &reply,
+ "s", session_id);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_read(reply, "o", &ans);
+ if (r < 0)
+ return r;
+
+ ans = strdup(ans);
+ if (!ans)
+ return -ENOMEM;
+
+ *path = ans;
+ return 0;
+}
+
static int list_sessions(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
return bus_log_parse_error(r);
if (arg_legend)
- printf("%10s %10s %-16s %-16s\n", "SESSION", "UID", "USER", "SEAT");
+ printf("%10s %10s %-16s %-16s %-16s\n", "SESSION", "UID", "USER", "SEAT", "TTY");
while ((r = sd_bus_message_read(reply, "(susso)", &id, &uid, &user, &seat, &object)) > 0) {
- printf("%10s %10u %-16s %-16s\n", id, (unsigned) uid, user, seat);
+ _cleanup_(sd_bus_error_free) sd_bus_error error2 = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply2 = NULL;
+ _cleanup_free_ char *path = NULL;
+ const char *tty = NULL;
+
+ r = get_session_path(bus, id, &error2, &path);
+ if (r < 0)
+ log_warning("Failed to get session path: %s", bus_error_message(&error, r));
+ else {
+ r = sd_bus_get_property(
+ bus,
+ "org.freedesktop.login1",
+ path,
+ "org.freedesktop.login1.Session",
+ "TTY",
+ &error2,
+ &reply2,
+ "s");
+ if (r < 0)
+ log_warning("Failed to get TTY for session %s: %s",
+ id, bus_error_message(&error2, r));
+ else {
+ r = sd_bus_message_read(reply2, "s", &tty);
+ if (r < 0)
+ return bus_log_parse_error(r);
+ }
+ }
+
+ printf("%10s %10"PRIu32" %-16s %-16s %-16s\n", id, uid, user, seat, strna(tty));
k++;
}
if (r < 0)
printf("%10s %-16s\n", "UID", "USER");
while ((r = sd_bus_message_read(reply, "(uso)", &uid, &user, &object)) > 0) {
- printf("%10u %-16s\n", (unsigned) uid, user);
+ printf("%10"PRIu32" %-16s\n", uid, user);
k++;
}
if (r < 0)
printf("%s - ", strna(i.id));
if (i.name)
- printf("%s (%u)\n", i.name, (unsigned) i.uid);
+ printf("%s (%"PRIu32")\n", i.name, i.uid);
else
- printf("%u\n", (unsigned) i.uid);
+ printf("%"PRIu32"\n", i.uid);
s1 = format_timestamp_relative(since1, sizeof(since1), i.timestamp.realtime);
s2 = format_timestamp(since2, sizeof(since2), i.timestamp.realtime);
if (i.leader > 0) {
_cleanup_free_ char *t = NULL;
- printf("\t Leader: %u", (unsigned) i.leader);
+ printf("\t Leader: %"PRIu32, i.leader);
get_process_comm(i.leader, &t);
if (t)
*new_line = true;
if (i.name)
- printf("%s (%u)\n", i.name, (unsigned) i.uid);
+ printf("%s (%"PRIu32")\n", i.name, i.uid);
else
- printf("%u\n", (unsigned) i.uid);
+ printf("%"PRIu32"\n", i.uid);
s1 = format_timestamp_relative(since1, sizeof(since1), i.timestamp.realtime);
s2 = format_timestamp(since2, sizeof(since2), i.timestamp.realtime);
for (i = 1; i < argc; i++) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_(sd_bus_message_unrefp) sd_bus_message * reply = NULL;
- const char *path = NULL;
+ _cleanup_free_ char *path = NULL;
- r = sd_bus_call_method(
- bus,
- "org.freedesktop.login1",
- "/org/freedesktop/login1",
- "org.freedesktop.login1.Manager",
- "GetSession",
- &error, &reply,
- "s", argv[i]);
+ r = get_session_path(bus, argv[1], &error, &path);
if (r < 0) {
- log_error("Failed to get session: %s", bus_error_message(&error, r));
+ log_error("Failed to get session path: %s", bus_error_message(&error, r));
return r;
}
- r = sd_bus_message_read(reply, "o", &path);
- if (r < 0)
- return bus_log_parse_error(r);
-
if (properties)
r = show_properties(bus, path, &new_line);
else
return NULL;
b->name = strdup(name);
- if (!b->name) {
- free(b);
- return NULL;
- }
+ if (!b->name)
+ return mfree(b);
if (hashmap_put(m->buttons, b->name, b) < 0) {
free(b->name);
- free(b);
- return NULL;
+ return mfree(b);
}
b->manager = m;
return NULL;
d->sysfs = strdup(sysfs);
- if (!d->sysfs) {
- free(d);
- return NULL;
- }
+ if (!d->sysfs)
+ return mfree(d);
if (hashmap_put(m->devices, d->sysfs, d) < 0) {
free(d->sysfs);
- free(d);
- return NULL;
+ return mfree(d);
}
d->manager = m;
return NULL;
i->state_file = strappend("/run/systemd/inhibit/", id);
- if (!i->state_file) {
- free(i);
- return NULL;
- }
+ if (!i->state_file)
+ return mfree(i);
i->id = basename(i->state_file);
if (hashmap_put(m->inhibitors, i->id, i) < 0) {
free(i->state_file);
- free(i);
- return NULL;
+ return mfree(i);
}
i->manager = m;
return NULL;
s->state_file = strappend("/run/systemd/seats/", id);
- if (!s->state_file) {
- free(s);
- return NULL;
- }
+ if (!s->state_file)
+ return mfree(s);
s->id = basename(s->state_file);
s->manager = m;
if (hashmap_put(m->seats, s->id, s) < 0) {
free(s->state_file);
- free(s);
- return NULL;
+ return mfree(s);
}
return s;
#if 1 /// Additionally elogind saves the user state file
user_save(s->user);
#endif // 1
-
p = session_bus_path(s);
if (!p)
return -ENOMEM;
return NULL;
s->state_file = strappend("/run/systemd/sessions/", id);
- if (!s->state_file) {
- free(s);
- return NULL;
- }
+ if (!s->state_file)
+ return mfree(s);
s->devices = hashmap_new(&devt_hash_ops);
if (!s->devices) {
free(s->state_file);
- free(s);
- return NULL;
+ return mfree(s);
}
s->id = basename(s->state_file);
if (hashmap_put(m->sessions, s->id, s) < 0) {
hashmap_free(s->devices);
free(s->state_file);
- free(s);
- return NULL;
+ return mfree(s);
}
s->manager = m;
return 0;
/* Let's always abandon the scope first. This tells systemd that we are not interested anymore, and everything
- * that is left in in the scope is "left-over". Informing systemd about this has the benefit that it will log
+ * that is left in the scope is "left-over". Informing systemd about this has the benefit that it will log
* when killing any processes left after this point. */
r = manager_abandon_scope(s->manager, s->scope, &error);
if (r < 0)
#if 1 /// elogind uses a static value here
# define SPECIAL_USER_SLICE "user.slice"
#endif // 1
-
int user_new(User **out, Manager *m, uid_t uid, gid_t gid, const char *name) {
_cleanup_(user_freep) User *u = NULL;
char lu[DECIMAL_STR_MAX(uid_t) + 1];
r = mount("tmpfs", u->runtime_path, "tmpfs", MS_NODEV|MS_NOSUID, t);
if (r < 0) {
- if (errno != EPERM) {
+ if (errno != EPERM && errno != EACCES) {
r = log_error_errno(errno, "Failed to mount per-user tmpfs directory %s: %m", u->runtime_path);
goto fail;
}
- /* Lacking permissions, maybe
- * CAP_SYS_ADMIN-less container? In this case,
- * just use a normal directory. */
+ log_debug_errno(errno, "Failed to mount per-user tmpfs directory %s, assuming containerized execution, ignoring: %m", u->runtime_path);
r = chmod_and_chown(u->runtime_path, 0700, u->uid, u->gid);
if (r < 0) {
if (k < 0)
r = k;
- /* Clean SysV + POSIX IPC objects */
- if (u->manager->remove_ipc) {
- k = clean_ipc(u->uid);
+ /* Clean SysV + POSIX IPC objects, but only if this is not a system user. Background: in many setups cronjobs
+ * are run in full PAM and thus logind sessions, even if the code run doesn't belong to actual users but to
+ * system components. Since enable RemoveIPC= globally for all users, we need to be a bit careful with such
+ * cases, as we shouldn't accidentally remove a system service's IPC objects while it is running, just because
+ * a cronjob running as the same user just finished. Hence: exclude system users generally from IPC clean-up,
+ * and do it only for normal users. */
+ if (u->manager->remove_ipc && u->uid > SYSTEM_UID_MAX) {
+ k = clean_ipc_by_uid(u->uid);
if (k < 0)
r = k;
}
assert(rvalue);
assert(data);
- /* First, try to parse as percentage */
+ if (isempty(rvalue)) {
+ *m = system_tasks_max_scale(DEFAULT_USER_TASKS_MAX_PERCENTAGE, 100U);
+ return 0;
+ }
+
+ if (streq(rvalue, "infinity")) {
+ *m = CGROUP_LIMIT_MAX;
+ return 0;
+ }
+
+ /* Try to parse as percentage */
r = parse_percent(rvalue);
if (r >= 0)
k = system_tasks_max_scale(r, 100U);
static int warn_wall(Manager *m, usec_t n) {
char date[FORMAT_TIMESTAMP_MAX] = {};
_cleanup_free_ char *l = NULL;
- bool left;
+ usec_t left;
int r;
assert(m);
if (!m->enable_wall_messages)
return 0;
#endif // 1
-
n = now(CLOCK_REALTIME);
elapse = m->scheduled_shutdown_timeout;
#include "signal-util.h"
#include "strv.h"
#include "udev-util.h"
-
/// Additional includes needed by elogind
#include "cgroup.h" // From src/core/
#include "elogind.h"
m->idle_action = HANDLE_IGNORE;
m->runtime_dir_size = physical_memory_scale(10U, 100U); /* 10% */
- m->user_tasks_max = system_tasks_max_scale(33U, 100U); /* 33% */
+ m->user_tasks_max = system_tasks_max_scale(DEFAULT_USER_TASKS_MAX_PERCENTAGE, 100U); /* 33% */
m->sessions_max = 8192;
m->inhibitors_max = 8192;
#if 0 /// elogind parses its own config file
assert(m);
- return config_parse_many(PKGSYSCONFDIR "/logind.conf",
+ return config_parse_many_nulstr(PKGSYSCONFDIR "/logind.conf",
CONF_PATHS_NULSTR("systemd/logind.conf.d"),
"Login\0",
config_item_perf_lookup, logind_gperf_lookup,
int acl_search_groups(const char *path, char ***ret_groups) {
_cleanup_strv_free_ char **g = NULL;
- _cleanup_(acl_freep) acl_t acl = NULL;
+ _cleanup_(acl_free) acl_t acl = NULL;
bool ret = false;
acl_entry_t entry;
int r;
return r;
switch (type) {
+
case SD_BUS_TYPE_STRING: {
- const char *s;
char **p = userdata;
+ const char *s;
r = sd_bus_message_read_basic(m, type, &s);
if (r < 0)
- break;
+ return r;
if (isempty(s))
- break;
+ s = NULL;
- r = free_and_strdup(p, s);
- break;
+ return free_and_strdup(p, s);
}
case SD_BUS_TYPE_ARRAY: {
r = bus_message_read_strv_extend(m, &l);
if (r < 0)
- break;
+ return r;
strv_free(*p);
*p = l;
l = NULL;
-
- break;
+ return 0;
}
case SD_BUS_TYPE_BOOLEAN: {
r = sd_bus_message_read_basic(m, type, &b);
if (r < 0)
- break;
+ return r;
*p = b;
-
- break;
+ return 0;
}
+ case SD_BUS_TYPE_INT32:
case SD_BUS_TYPE_UINT32: {
- uint32_t u;
- uint32_t *p = userdata;
+ uint32_t u, *p = userdata;
r = sd_bus_message_read_basic(m, type, &u);
if (r < 0)
- break;
+ return r;
*p = u;
-
- break;
+ return 0;
}
+ case SD_BUS_TYPE_INT64:
case SD_BUS_TYPE_UINT64: {
- uint64_t t;
- uint64_t *p = userdata;
+ uint64_t t, *p = userdata;
r = sd_bus_message_read_basic(m, type, &t);
if (r < 0)
- break;
+ return r;
*p = t;
-
- break;
+ return 0;
}
case SD_BUS_TYPE_DOUBLE: {
- double d;
- double *p = userdata;
+ double d, *p = userdata;
r = sd_bus_message_read_basic(m, type, &d);
if (r < 0)
- break;
+ return r;
*p = d;
+ return 0;
+ }}
- break;
- }
-
- default:
- break;
- }
-
- return r;
+ return -EOPNOTSUPP;
}
int bus_message_map_all_properties(
return bus_message_map_all_properties(m, map, userdata);
}
-int bus_connect_transport(BusTransport transport, const char *host, bool user, sd_bus **bus) {
+int bus_connect_transport(BusTransport transport, const char *host, bool user, sd_bus **ret) {
+ _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
int r;
assert(transport >= 0);
assert(transport < _BUS_TRANSPORT_MAX);
- assert(bus);
+ assert(ret);
assert_return((transport == BUS_TRANSPORT_LOCAL) == !host, -EINVAL);
assert_return(transport == BUS_TRANSPORT_LOCAL || !user, -EOPNOTSUPP);
case BUS_TRANSPORT_LOCAL:
#if 0 /// elogind does not support a user bus
if (user)
- r = sd_bus_default_user(bus);
+ r = sd_bus_default_user(&bus);
else
#endif // 0
- r = sd_bus_default_system(bus);
+ r = sd_bus_default_system(&bus);
break;
case BUS_TRANSPORT_REMOTE:
- r = sd_bus_open_system_remote(bus, host);
+ r = sd_bus_open_system_remote(&bus, host);
break;
case BUS_TRANSPORT_MACHINE:
- r = sd_bus_open_system_machine(bus, host);
+ r = sd_bus_open_system_machine(&bus, host);
break;
default:
assert_not_reached("Hmm, unknown transport type.");
}
+ if (r < 0)
+ return r;
- return r;
+ r = sd_bus_set_exit_on_disconnect(bus, true);
+ if (r < 0)
+ return r;
+
+ *ret = bus;
+ bus = NULL;
+
+ return 0;
}
#if 0 /// UNNEEDED by elogind
return sd_bus_message_append_basic(reply, 'b', &b);
}
+#if 0 /// UNNEEDED by elogind
+int bus_property_get_id128(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ sd_id128_t *id = userdata;
+
+ if (sd_id128_is_null(*id)) /* Add an empty array if the ID is zero */
+ return sd_bus_message_append(reply, "ay", 0);
+ else
+ return sd_bus_message_append_array(reply, 'y', id->bytes, 16);
+}
+#endif // 0
+
#if __SIZEOF_SIZE_T__ != 8
int bus_property_get_size(
sd_bus *bus,
int bus_print_all_properties(sd_bus *bus, const char *dest, const char *path, char **filter, bool value, bool all);
int bus_property_get_bool(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error);
+#if 0 /// UNNEEDED by elogind
+int bus_property_get_id128(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error);
+#endif // 0
#define bus_property_get_usec ((sd_bus_property_get_t) NULL)
#define bus_property_set_usec ((sd_bus_property_set_t) NULL)
#include "macro.h"
#include "string-util.h"
#include "strv.h"
+#include "user-util.h"
-static int clean_sysvipc_shm(uid_t delete_uid) {
+static bool match_uid_gid(uid_t subject_uid, gid_t subject_gid, uid_t delete_uid, gid_t delete_gid) {
+
+ if (uid_is_valid(delete_uid) && subject_uid == delete_uid)
+ return true;
+
+ if (gid_is_valid(delete_gid) && subject_gid == delete_gid)
+ return true;
+
+ return false;
+}
+
+static int clean_sysvipc_shm(uid_t delete_uid, gid_t delete_gid) {
_cleanup_fclose_ FILE *f = NULL;
char line[LINE_MAX];
bool first = true;
if (n_attached > 0)
continue;
- if (uid != delete_uid)
+ if (!match_uid_gid(uid, gid, delete_uid, delete_gid))
continue;
if (shmctl(shmid, IPC_RMID, NULL) < 0) {
ret = log_warning_errno(errno,
"Failed to remove SysV shared memory segment %i: %m",
shmid);
- }
+ } else
+ log_debug("Removed SysV shared memory segment %i.", shmid);
}
return ret;
return log_warning_errno(errno, "Failed to read /proc/sysvipc/shm: %m");
}
-static int clean_sysvipc_sem(uid_t delete_uid) {
+static int clean_sysvipc_sem(uid_t delete_uid, gid_t delete_gid) {
_cleanup_fclose_ FILE *f = NULL;
char line[LINE_MAX];
bool first = true;
&semid, &uid, &gid, &cuid, &cgid) != 5)
continue;
- if (uid != delete_uid)
+ if (!match_uid_gid(uid, gid, delete_uid, delete_gid))
continue;
if (semctl(semid, 0, IPC_RMID) < 0) {
ret = log_warning_errno(errno,
"Failed to remove SysV semaphores object %i: %m",
semid);
- }
+ } else
+ log_debug("Removed SysV semaphore %i.", semid);
}
return ret;
return log_warning_errno(errno, "Failed to read /proc/sysvipc/sem: %m");
}
-static int clean_sysvipc_msg(uid_t delete_uid) {
+static int clean_sysvipc_msg(uid_t delete_uid, gid_t delete_gid) {
_cleanup_fclose_ FILE *f = NULL;
char line[LINE_MAX];
bool first = true;
&msgid, &cpid, &lpid, &uid, &gid, &cuid, &cgid) != 7)
continue;
- if (uid != delete_uid)
+ if (!match_uid_gid(uid, gid, delete_uid, delete_gid))
continue;
if (msgctl(msgid, IPC_RMID, NULL) < 0) {
ret = log_warning_errno(errno,
"Failed to remove SysV message queue %i: %m",
msgid);
- }
+ } else
+ log_debug("Removed SysV message queue %i.", msgid);
}
return ret;
return log_warning_errno(errno, "Failed to read /proc/sysvipc/msg: %m");
}
-static int clean_posix_shm_internal(DIR *dir, uid_t uid) {
+static int clean_posix_shm_internal(DIR *dir, uid_t uid, gid_t gid) {
struct dirent *de;
int ret = 0, r;
assert(dir);
- FOREACH_DIRENT(de, dir, goto fail) {
+ FOREACH_DIRENT_ALL(de, dir, goto fail) {
struct stat st;
if (STR_IN_SET(de->d_name, "..", "."))
if (errno == ENOENT)
continue;
- log_warning_errno(errno, "Failed to stat() POSIX shared memory segment %s: %m", de->d_name);
- ret = -errno;
+ ret = log_warning_errno(errno, "Failed to stat() POSIX shared memory segment %s: %m", de->d_name);
continue;
}
- if (st.st_uid != uid)
+ if (!match_uid_gid(st.st_uid, st.st_gid, uid, gid))
continue;
if (S_ISDIR(st.st_mode)) {
kid = xopendirat(dirfd(dir), de->d_name, O_NOFOLLOW|O_NOATIME);
if (!kid) {
- if (errno != ENOENT) {
- log_warning_errno(errno, "Failed to enter shared memory directory %s: %m", de->d_name);
- ret = -errno;
- }
+ if (errno != ENOENT)
+ ret = log_warning_errno(errno, "Failed to enter shared memory directory %s: %m", de->d_name);
} else {
- r = clean_posix_shm_internal(kid, uid);
+ r = clean_posix_shm_internal(kid, uid, gid);
if (r < 0)
ret = r;
}
if (errno == ENOENT)
continue;
- log_warning_errno(errno, "Failed to remove POSIX shared memory directory %s: %m", de->d_name);
- ret = -errno;
- }
+ ret = log_warning_errno(errno, "Failed to remove POSIX shared memory directory %s: %m", de->d_name);
+ } else
+ log_debug("Removed POSIX shared memory directory %s", de->d_name);
} else {
if (unlinkat(dirfd(dir), de->d_name, 0) < 0) {
if (errno == ENOENT)
continue;
- log_warning_errno(errno, "Failed to remove POSIX shared memory segment %s: %m", de->d_name);
- ret = -errno;
- }
+ ret = log_warning_errno(errno, "Failed to remove POSIX shared memory segment %s: %m", de->d_name);
+ } else
+ log_debug("Removed POSIX shared memory segment %s", de->d_name);
}
}
return ret;
fail:
- log_warning_errno(errno, "Failed to read /dev/shm: %m");
- return -errno;
+ return log_warning_errno(errno, "Failed to read /dev/shm: %m");
}
-static int clean_posix_shm(uid_t uid) {
+static int clean_posix_shm(uid_t uid, gid_t gid) {
_cleanup_closedir_ DIR *dir = NULL;
dir = opendir("/dev/shm");
return log_warning_errno(errno, "Failed to open /dev/shm: %m");
}
- return clean_posix_shm_internal(dir, uid);
+ return clean_posix_shm_internal(dir, uid, gid);
}
#if 0 /// UNNEEDED by elogind
-static int clean_posix_mq(uid_t uid) {
+static int clean_posix_mq(uid_t uid, gid_t gid) {
_cleanup_closedir_ DIR *dir = NULL;
struct dirent *de;
int ret = 0;
return log_warning_errno(errno, "Failed to open /dev/mqueue: %m");
}
- FOREACH_DIRENT(de, dir, goto fail) {
+ FOREACH_DIRENT_ALL(de, dir, goto fail) {
struct stat st;
char fn[1+strlen(de->d_name)+1];
continue;
}
- if (st.st_uid != uid)
+ if (!match_uid_gid(st.st_uid, st.st_gid, uid, gid))
continue;
fn[0] = '/';
ret = log_warning_errno(errno,
"Failed to unlink POSIX message queue %s: %m",
fn);
- }
+ } else
+ log_debug("Removed POSIX message queue %s", fn);
}
return ret;
}
#endif // 0
-int clean_ipc(uid_t uid) {
+int clean_ipc(uid_t uid, gid_t gid) {
int ret = 0, r;
- /* Refuse to clean IPC of the root and system users */
- if (uid <= SYSTEM_UID_MAX)
+ /* Anything to do? */
+ if (!uid_is_valid(uid) && !gid_is_valid(gid))
+ return 0;
+
+ /* Refuse to clean IPC of the root user */
+ if (uid == 0 && gid == 0)
return 0;
- r = clean_sysvipc_shm(uid);
+ r = clean_sysvipc_shm(uid, gid);
if (r < 0)
ret = r;
- r = clean_sysvipc_sem(uid);
+ r = clean_sysvipc_sem(uid, gid);
if (r < 0)
ret = r;
- r = clean_sysvipc_msg(uid);
+ r = clean_sysvipc_msg(uid, gid);
if (r < 0)
ret = r;
- r = clean_posix_shm(uid);
+ r = clean_posix_shm(uid, gid);
if (r < 0)
ret = r;
#if 0 /// elogind does not use mq_open anywhere
- r = clean_posix_mq(uid);
+ r = clean_posix_mq(uid, gid);
if (r < 0)
ret = r;
#endif // 0
return ret;
}
+
+int clean_ipc_by_uid(uid_t uid) {
+ return clean_ipc(uid, GID_INVALID);
+}
+
+#if 0 /// UNNEEDED by elogind
+int clean_ipc_by_gid(gid_t gid) {
+ return clean_ipc(UID_INVALID, gid);
+}
+#endif // 0
#include <sys/types.h>
-int clean_ipc(uid_t uid);
+int clean_ipc(uid_t uid, gid_t gid);
+int clean_ipc_by_uid(uid_t uid);
+#if 0 /// UNNEEDED by elogind
+int clean_ipc_by_gid(gid_t gid);
+#endif // 0
return 0;
}
-/* Parse each config file in the specified directories. */
-int config_parse_many(const char *conf_file,
- const char *conf_file_dirs,
- const char *sections,
- ConfigItemLookup lookup,
- const void *table,
- bool relaxed,
- void *userdata) {
- _cleanup_strv_free_ char **files = NULL;
+static int config_parse_many_files(
+ const char *conf_file,
+ char **files,
+ const char *sections,
+ ConfigItemLookup lookup,
+ const void *table,
+ bool relaxed,
+ void *userdata) {
+
char **fn;
int r;
- r = conf_files_list_nulstr(&files, ".conf", NULL, conf_file_dirs);
- if (r < 0)
- return r;
-
if (conf_file) {
r = config_parse(NULL, conf_file, NULL, sections, lookup, table, relaxed, false, true, userdata);
if (r < 0)
return 0;
}
+/* Parse each config file in the directories specified as nulstr. */
+int config_parse_many_nulstr(
+ const char *conf_file,
+ const char *conf_file_dirs,
+ const char *sections,
+ ConfigItemLookup lookup,
+ const void *table,
+ bool relaxed,
+ void *userdata) {
+
+ _cleanup_strv_free_ char **files = NULL;
+ int r;
+
+ r = conf_files_list_nulstr(&files, ".conf", NULL, conf_file_dirs);
+ if (r < 0)
+ return r;
+
+ return config_parse_many_files(conf_file, files,
+ sections, lookup, table, relaxed, userdata);
+}
+
+#if 0 /// UNNEEDED by elogind
+/* Parse each config file in the directories specified as strv. */
+int config_parse_many(
+ const char *conf_file,
+ const char* const* conf_file_dirs,
+ const char *dropin_dirname,
+ const char *sections,
+ ConfigItemLookup lookup,
+ const void *table,
+ bool relaxed,
+ void *userdata) {
+
+ _cleanup_strv_free_ char **dropin_dirs = NULL;
+ _cleanup_strv_free_ char **files = NULL;
+ const char *suffix;
+ int r;
+
+ suffix = strjoina("/", dropin_dirname);
+ r = strv_extend_strv_concat(&dropin_dirs, (char**) conf_file_dirs, suffix);
+ if (r < 0)
+ return r;
+
+ r = conf_files_list_strv(&files, ".conf", NULL, (const char* const*) dropin_dirs);
+ if (r < 0)
+ return r;
+
+ return config_parse_many_files(conf_file, files,
+ sections, lookup, table, relaxed, userdata);
+}
+#endif // 0
+
#define DEFINE_PARSER(type, vartype, conv_func) \
int config_parse_##type( \
const char *unit, \
DEFINE_PARSER(int, int, safe_atoi);
DEFINE_PARSER(long, long, safe_atoli);
+#if 0 /// UNNEEDED by elogind
+DEFINE_PARSER(uint16, uint16_t, safe_atou16);
DEFINE_PARSER(uint32, uint32_t, safe_atou32);
+#endif // 0
DEFINE_PARSER(uint64, uint64_t, safe_atou64);
DEFINE_PARSER(unsigned, unsigned, safe_atou);
DEFINE_PARSER(double, double, safe_atod);
* ConfigPerfItem tables */
int config_item_perf_lookup(const void *table, const char *section, const char *lvalue, ConfigParserCallback *func, int *ltype, void **data, void *userdata);
-int config_parse(const char *unit,
- const char *filename,
- FILE *f,
- const char *sections, /* nulstr */
- ConfigItemLookup lookup,
- const void *table,
- bool relaxed,
- bool allow_include,
- bool warn,
- void *userdata);
-
-int config_parse_many(const char *conf_file, /* possibly NULL */
- const char *conf_file_dirs, /* nulstr */
- const char *sections, /* nulstr */
- ConfigItemLookup lookup,
- const void *table,
- bool relaxed,
- void *userdata);
+int config_parse(
+ const char *unit,
+ const char *filename,
+ FILE *f,
+ const char *sections, /* nulstr */
+ ConfigItemLookup lookup,
+ const void *table,
+ bool relaxed,
+ bool allow_include,
+ bool warn,
+ void *userdata);
+
+int config_parse_many_nulstr(
+ const char *conf_file, /* possibly NULL */
+ const char *conf_file_dirs, /* nulstr */
+ const char *sections, /* nulstr */
+ ConfigItemLookup lookup,
+ const void *table,
+ bool relaxed,
+ void *userdata);
+
+#if 0 /// UNNEEDED by elogind
+int config_parse_many(
+ const char *conf_file, /* possibly NULL */
+ const char* const* conf_file_dirs,
+ const char *dropin_dirname,
+ const char *sections, /* nulstr */
+ ConfigItemLookup lookup,
+ const void *table,
+ bool relaxed,
+ void *userdata);
+#endif // 0
/* Generic parsers */
int config_parse_int(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_unsigned(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_long(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+#if 0 /// UNNEEDED by elogind
+int config_parse_uint16(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_uint32(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+#endif // 0
int config_parse_uint64(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_double(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_iec_size(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
#include "process-util.h"
#include "signal-util.h"
#include "string-util.h"
+#include "strv.h"
#include "terminal-util.h"
static pid_t pager_pid = 0;
pager = getenv("PAGER");
/* If the pager is explicitly turned off, honour it */
- if (pager && (pager[0] == 0 || streq(pager, "cat")))
+ if (pager && STR_IN_SET(pager, "", "cat"))
return 0;
/* Determine and cache number of columns before we spawn the
{}
};
- config_parse_many(PKGSYSCONFDIR "/sleep.conf",
+ config_parse_many_nulstr(PKGSYSCONFDIR "/sleep.conf",
CONF_PATHS_NULSTR("systemd/sleep.conf.d"),
"Sleep\0", config_item_table_lookup, items,
false, NULL);
#if 0 /// UNNEEDED by elogind
int sd_bus_get_allow_interactive_authorization(sd_bus *bus);
#endif // 0
+int sd_bus_set_exit_on_disconnect(sd_bus *bus, int b);
+#if 0 /// UNNEEDED by elogind
+int sd_bus_get_exit_on_disconnect(sd_bus *bus);
+#endif // 0
int sd_bus_start(sd_bus *ret);
int sd_bus_track_add_name(sd_bus_track *track, const char *name);
int sd_bus_track_remove_name(sd_bus_track *track, const char *name);
+#if 0 /// UNNEEDED by elogind
+int sd_bus_track_set_recursive(sd_bus_track *track, int b);
+int sd_bus_track_get_recursive(sd_bus_track *track);
+#endif // 0
+
unsigned sd_bus_track_count(sd_bus_track *track);
-const char* sd_bus_track_contains(sd_bus_track *track, const char *names);
+#if 0 /// UNNEEDED by elogind
+int sd_bus_track_count_sender(sd_bus_track *track, sd_bus_message *m);
+int sd_bus_track_count_name(sd_bus_track *track, const char *name);
+#endif // 0
+
+const char* sd_bus_track_contains(sd_bus_track *track, const char *name);
const char* sd_bus_track_first(sd_bus_track *track);
const char* sd_bus_track_next(sd_bus_track *track);
int sd_id128_randomize(sd_id128_t *ret);
int sd_id128_get_machine(sd_id128_t *ret);
-
int sd_id128_get_boot(sd_id128_t *ret);
+int sd_id128_get_invocation(sd_id128_t *ret);
#define SD_ID128_MAKE(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) \
((const sd_id128_t) { .bytes = { 0x##v0, 0x##v1, 0x##v2, 0x##v3, 0x##v4, 0x##v5, 0x##v6, 0x##v7, \
#define SD_MESSAGE_JOURNAL_USAGE SD_ID128_MAKE(ec,38,7f,57,7b,84,4b,8f,a9,48,f3,3c,ad,9a,75,e6)
#define SD_MESSAGE_COREDUMP SD_ID128_MAKE(fc,2e,22,bc,6e,e6,47,b6,b9,07,29,ab,34,a2,50,b1)
+#define SD_MESSAGE_TRUNCATED_CORE SD_ID128_MAKE(5a,ad,d8,e9,54,dc,4b,1a,8c,95,4d,63,fd,9e,11,37)
#define SD_MESSAGE_SESSION_START SD_ID128_MAKE(8d,45,62,0c,1a,43,48,db,b1,74,10,da,57,c6,0c,66)
#define SD_MESSAGE_SESSION_STOP SD_ID128_MAKE(33,54,93,94,24,b4,45,6d,98,02,ca,83,33,ed,42,4a)
if (r < 0)
return log_warning_errno(r, "Failed to get state: %s", bus_error_message(&error, r));
- if (streq(state, "active") || streq(state, "reloading"))
+ if (STR_IN_SET(state, "active", "reloading"))
return table[i].runlevel;
}