-import itertools
+#!/usr/bin/env python
+
+# SPDX-License-Identifier: Unlicense
+#
+# Based on the template file provided by the 'YCM-Generator' project authored by
+# Reuben D'Netto.
+# Jiahui Xie has re-reformatted and expanded the original script in accordance
+# to the requirements of the PEP 8 style guide and 'systemd' project,
+# respectively.
+#
+# The original license is preserved as it is.
+#
+#
+# This is free and unencumbered software released into the public domain.
+#
+# Anyone is free to copy, modify, publish, use, compile, sell, or
+# distribute this software, either in source code form or as a compiled
+# binary, for any purpose, commercial or non-commercial, and by any
+# means.
+#
+# In jurisdictions that recognize copyright laws, the author or authors
+# of this software dedicate any and all copyright interest in the
+# software to the public domain. We make this dedication for the benefit
+# of the public at large and to the detriment of our heirs and
+# successors. We intend this dedication to be an overt act of
+# relinquishment in perpetuity of all present and future rights to this
+# software under copyright law.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+# OTHER DEALINGS IN THE SOFTWARE.
+#
+# For more information, please refer to <http://unlicense.org/>
+
+"""
+YouCompleteMe configuration file tailored to support the 'meson' build system
+used by the 'systemd' project.
+"""
+
+import glob
import os
-import subprocess
+import ycm_core
-def GetFlagsFromMakefile(varname):
- return subprocess.check_output([
- "make", "-s", "print-%s" % varname]).decode().split()
-
-def Flatten(lists):
- return list(itertools.chain.from_iterable(lists))
+SOURCE_EXTENSIONS = (".C", ".cpp", ".cxx", ".cc", ".c", ".m", ".mm")
+HEADER_EXTENSIONS = (".H", ".h", ".hxx", ".hpp", ".hh")
def DirectoryOfThisScript():
- return os.path.dirname(os.path.abspath(__file__))
+ """
+ Return the absolute path of the parent directory containing this
+ script.
+ """
+ return os.path.dirname(os.path.abspath(__file__))
+
+
+def GuessBuildDirectory():
+ """
+ Guess the build directory using the following heuristics:
+
+ 1. Returns the current directory of this script plus 'build'
+ subdirectory in absolute path if this subdirectory exists.
+
+ 2. Otherwise, probes whether there exists any directory
+ containing '.ninja_log' file two levels above the current directory;
+ returns this single directory only if there is one candidate.
+ """
+ result = os.path.join(DirectoryOfThisScript(), "build")
+
+ if os.path.exists(result):
+ return result
+
+ result = glob.glob(os.path.join(DirectoryOfThisScript(),
+ "..", "..", "*", ".ninja_log"))
+
+ if not result:
+ return ""
+
+ if 1 != len(result):
+ return ""
+
+ return os.path.split(result[0])[0]
+
+
+def TraverseByDepth(root, include_extensions):
+ """
+ Return a set of child directories of the 'root' containing file
+ extensions specified in 'include_extensions'.
+
+ NOTE:
+ 1. The 'root' directory itself is excluded from the result set.
+ 2. No subdirectories would be excluded if 'include_extensions' is left
+ to 'None'.
+ 3. Each entry in 'include_extensions' must begin with string '.'.
+ """
+ is_root = True
+ result = set()
+ # Perform a depth first top down traverse of the given directory tree.
+ for root_dir, subdirs, file_list in os.walk(root):
+ if not is_root:
+ # print("Relative Root: ", root_dir)
+ # print(subdirs)
+ if include_extensions:
+ get_ext = os.path.splitext
+ subdir_extensions = {
+ get_ext(f)[-1] for f in file_list if get_ext(f)[-1]
+ }
+ if subdir_extensions & include_extensions:
+ result.add(root_dir)
+ else:
+ result.add(root_dir)
+ else:
+ is_root = False
+
+ return result
+
+
+_project_src_dir = os.path.join(DirectoryOfThisScript(), "src")
+_include_dirs_set = TraverseByDepth(_project_src_dir, frozenset({".h"}))
+flags = [
+ "-x",
+ "c"
+ # The following flags are partially redundant due to the existence of
+ # 'compile_commands.json'.
+ # '-Wall',
+ # '-Wextra',
+ # '-Wfloat-equal',
+ # '-Wpointer-arith',
+ # '-Wshadow',
+ # '-std=gnu99',
+]
+
+for include_dir in _include_dirs_set:
+ flags.append("-I" + include_dir)
+
+# Set this to the absolute path to the folder (NOT the file!) containing the
+# compile_commands.json file to use that instead of 'flags'. See here for
+# more details: http://clang.llvm.org/docs/JSONCompilationDatabase.html
+#
+# You can get CMake to generate this file for you by adding:
+# set( CMAKE_EXPORT_COMPILE_COMMANDS 1 )
+# to your CMakeLists.txt file.
+#
+# Most projects will NOT need to set this to anything; you can just change the
+# 'flags' list of compilation flags. Notice that YCM itself uses that approach.
+compilation_database_folder = GuessBuildDirectory()
+
+if os.path.exists(compilation_database_folder):
+ database = ycm_core.CompilationDatabase(compilation_database_folder)
+else:
+ database = None
def MakeRelativePathsInFlagsAbsolute(flags, working_directory):
- if not working_directory:
- return flags
- new_flags = []
- make_next_absolute = False
- path_flags = [ '-isystem', '-I', '-iquote', '--sysroot=' ]
- for flag in flags:
- new_flag = flag
-
- if make_next_absolute:
- make_next_absolute = False
- if not flag.startswith('/'):
- new_flag = os.path.join(working_directory, flag)
-
- for path_flag in path_flags:
- if flag == path_flag:
- make_next_absolute = True
- break
-
- if flag.startswith(path_flag):
- path = flag[ len(path_flag): ]
- new_flag = path_flag + os.path.join(working_directory, path)
- break
-
- if new_flag:
- new_flags.append(new_flag)
- return new_flags
-
-
-def FlagsForFile(filename):
- relative_to = DirectoryOfThisScript()
-
- return {
- 'flags': MakeRelativePathsInFlagsAbsolute(flags, relative_to),
- 'do_cache': True
- }
-
-flags = Flatten(map(GetFlagsFromMakefile, [
- 'AM_CPPFLAGS',
- 'CPPFLAGS',
- 'AM_CFLAGS',
- 'CFLAGS',
-]))
-
-# these flags cause crashes in libclang, so remove them
-flags.remove('-Wlogical-op')
-flags.remove('-Wsuggest-attribute=noreturn')
-flags.remove('-Wdate-time')
-
-# vim: set et ts=2 sw=2:
+ """
+ Iterate through 'flags' and replace the relative paths prefixed by
+ '-isystem', '-I', '-iquote', '--sysroot=' with absolute paths
+ start with 'working_directory'.
+ """
+ if not working_directory:
+ return list(flags)
+ new_flags = []
+ make_next_absolute = False
+ path_flags = ["-isystem", "-I", "-iquote", "--sysroot="]
+ for flag in flags:
+ new_flag = flag
+
+ if make_next_absolute:
+ make_next_absolute = False
+ if not flag.startswith("/"):
+ new_flag = os.path.join(working_directory, flag)
+
+ for path_flag in path_flags:
+ if flag == path_flag:
+ make_next_absolute = True
+ break
+
+ if flag.startswith(path_flag):
+ path = flag[len(path_flag):]
+ new_flag = path_flag + os.path.join(working_directory, path)
+ break
+
+ if new_flag:
+ new_flags.append(new_flag)
+ return new_flags
+
+
+def IsHeaderFile(filename):
+ """
+ Check whether 'filename' is considered as a header file.
+ """
+ extension = os.path.splitext(filename)[1]
+ return extension in HEADER_EXTENSIONS
+
+
+def GetCompilationInfoForFile(filename):
+ """
+ Helper function to look up compilation info of 'filename' in the 'database'.
+ """
+ # The compilation_commands.json file generated by CMake does not have
+ # entries for header files. So we do our best by asking the db for flags for
+ # a corresponding source file, if any. If one exists, the flags for that
+ # file should be good enough.
+ if not database:
+ return None
+
+ if IsHeaderFile(filename):
+ basename = os.path.splitext(filename)[0]
+ for extension in SOURCE_EXTENSIONS:
+ replacement_file = basename + extension
+ if os.path.exists(replacement_file):
+ compilation_info = \
+ database.GetCompilationInfoForFile(replacement_file)
+ if compilation_info.compiler_flags_:
+ return compilation_info
+ return None
+ return database.GetCompilationInfoForFile(filename)
+
+
+def FlagsForFile(filename, **kwargs):
+ """
+ Callback function to be invoked by YouCompleteMe in order to get the
+ information necessary to compile 'filename'.
+
+ It returns a dictionary with a single element 'flags'. This element is a
+ list of compiler flags to pass to libclang for the file 'filename'.
+ """
+ if database:
+ # Bear in mind that compilation_info.compiler_flags_ does NOT return a
+ # python list, but a "list-like" StringVec object
+ compilation_info = GetCompilationInfoForFile(filename)
+ if not compilation_info:
+ return None
+
+ final_flags = MakeRelativePathsInFlagsAbsolute(
+ compilation_info.compiler_flags_,
+ compilation_info.compiler_working_dir_)
+
+ else:
+ relative_to = DirectoryOfThisScript()
+ final_flags = MakeRelativePathsInFlagsAbsolute(flags, relative_to)
+
+ return {
+ "flags": final_flags,
+ "do_cache": True
+ }
</varlistentry>
<varlistentry>
+ <term><option>-q</option></term>
<term><option>--quiet</option></term>
<listitem>
<term><option>-q</option></term>
<term><option>--quiet</option></term>
- <listitem><para>Suppresses info messages about lack
+ <listitem><para>Suppresses informational messages about lack
of access to journal files and possible in-flight coredumps.
</para></listitem>
</varlistentry>
<!ENTITY usergeneratordir @USER_GENERATOR_PATH@>
<!ENTITY systemenvgeneratordir @SYSTEM_ENV_GENERATOR_PATH@>
<!ENTITY userenvgeneratordir @USER_ENV_GENERATOR_PATH@>
+<!ENTITY CERTIFICATE_ROOT @CERTIFICATE_ROOT@>
<term><option>-q</option></term>
<term><option>--quiet</option></term>
- <listitem><para>Suppresses all info messages
+ <listitem><para>Suppresses all informational messages
(i.e. "-- Logs begin at …", "-- Reboot --"),
any warning messages regarding
inaccessible system journals when run as a normal
</varlistentry>
<xi:include href="user-system-options.xml" xpointer="host" />
+ <xi:include href="user-system-options.xml" xpointer="machine" />
<xi:include href="standard-options.xml" xpointer="help" />
<xi:include href="standard-options.xml" xpointer="version" />
<literal>,</literal> if another address will be output afterwards. </para></listitem>
</varlistentry>
+ <varlistentry>
+ <term><option>-q</option></term>
+ <term><option>--quiet</option></term>
+
+ <listitem><para>Suppresses additional informational output while running.</para></listitem>
+ </varlistentry>
+
<xi:include href="user-system-options.xml" xpointer="host" />
<varlistentry>
along with systemd; If not, see <http://www.gnu.org/licenses/>.
-->
-<refentry id="systemd-hwdb" conditional="ENABLE_HWDB">
+<refentry id="systemd-hwdb" conditional="ENABLE_HWDB"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
+
<refentryinfo>
<title>systemd-hwdb</title>
<productname>systemd</productname>
<refsect1><title>Options</title>
<variablelist>
- <varlistentry>
- <term><option>-h</option></term>
- <term><option>--help</option></term>
- <listitem>
- <para>Print help text.</para>
- </listitem>
- </varlistentry>
<varlistentry>
<term><option>--usr</option></term>
<listitem>
<para>Alternate root path in the filesystem.</para>
</listitem>
</varlistentry>
+
+ <xi:include href="standard-options.xml" xpointer="help" />
</variablelist>
<refsect2><title>systemd-hwdb
with <option>--cert=</option>.</para></listitem>
</varlistentry>
+ <varlistentry>
+ <term><option>--trust=</option></term>
+
+ <listitem><para>Specify the path to a file containing a
+ CA certificate in PEM format.</para></listitem>
+ </varlistentry>
+
<varlistentry>
<term><option>-D <replaceable>DIR</replaceable></option></term>
<term><option>--directory=<replaceable>DIR</replaceable></option></term>
<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
-"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd" [
+<!ENTITY % entities SYSTEM "custom-entities.ent" >
+%entities;
+]>
<!--
SPDX-License-Identifier: LGPL-2.1+
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><option>--key=</option></term>
+
+ <listitem><para>
+ Takes a path to a SSL key file in PEM format.
+ Defaults to <filename>&CERTIFICATE_ROOT;/private/journal-remote.pem</filename>.
+ This option can be used with <option>--listen-https=</option>.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--cert=</option></term>
+
+ <listitem><para>
+ Takes a path to a SSL certificate file in PEM format.
+ Defaults to <filename>&CERTIFICATE_ROOT;/certs/journal-remote.pem</filename>.
+ This option can be used with <option>--listen-https=</option>.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--trust=</option></term>
+
+ <listitem><para>
+ Takes a path to a SSL CA certificate file in PEM format,
+ or <option>all</option>. If <option>all</option> is set,
+ then certificate checking will be disabled.
+ Defaults to <filename>&CERTIFICATE_ROOT;/ca/trusted.pem</filename>.
+ This option can be used with <option>--listen-https=</option>.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--gnutls-log=</option></term>
+
+ <listitem><para>
+ Takes a comma separated list of gnutls logging categories.
+ This option can be used with <option>--listen-http=</option> or
+ <option>--listen-https=</option>.
+ </para></listitem>
+ </varlistentry>
+
</variablelist>
</refsect1>
<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
-"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd" [
+<!ENTITY % entities SYSTEM "custom-entities.ent" >
+%entities;
+]>
<!--
SPDX-License-Identifier: LGPL-2.1+
</para></listitem>
</varlistentry>
+ <varlistentry>
+ <term><option>--follow</option><optional>=<replaceable>BOOL</replaceable></optional></term>
+
+ <listitem><para>
+ If set to yes, then <command>systemd-journal-upload</command> waits for input.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--key=</option></term>
+
+ <listitem><para>
+ Takes a path to a SSL key file in PEM format.
+ Defaults to <filename>&CERTIFICATE_ROOT;/private/journal-upload.pem</filename>.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--cert=</option></term>
+
+ <listitem><para>
+ Takes a path to a SSL certificate file in PEM format.
+ Defaults to <filename>&CERTIFICATE_ROOT;/certs/journal-upload.pem</filename>.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--trust=</option></term>
+
+ <listitem><para>
+ Takes a path to a SSL CA certificate file in PEM format,
+ or <option>all</option>. If <option>all</option> is set,
+ then certificate checking will be disabled.
+ Defaults to <filename>&CERTIFICATE_ROOT;/ca/trusted.pem</filename>.
+ </para></listitem>
+ </varlistentry>
+
<xi:include href="standard-options.xml" xpointer="help" />
<xi:include href="standard-options.xml" xpointer="version" />
</variablelist>
<refsect1><title>Options</title>
<variablelist>
<varlistentry>
+ <term><option>-d</option></term>
<term><option>--daemon</option></term>
<listitem>
<para>Detach and run in the background.</para>
</varlistentry>
<varlistentry>
+ <term><option>-D</option></term>
<term><option>--debug</option></term>
<listitem>
<para>Print debug messages to standard error.</para>
</varlistentry>
<varlistentry>
+ <term><option>-c=</option></term>
<term><option>--children-max=</option></term>
<listitem>
<para>Limit the number of events executed in parallel.</para>
</varlistentry>
<varlistentry>
+ <term><option>-e=</option></term>
<term><option>--exec-delay=</option></term>
<listitem>
<para>Delay the execution of <varname>RUN</varname>
</varlistentry>
<varlistentry>
+ <term><option>-t=</option></term>
<term><option>--event-timeout=</option></term>
<listitem>
<para>Set the number of seconds to wait for events to finish. After
</varlistentry>
<varlistentry>
+ <term><option>-N=</option></term>
<term><option>--resolve-names=</option></term>
<listitem>
<para>Specify when systemd-udevd should resolve names of users and groups.
</listitem>
</varlistentry>
- <varlistentry>
- <term><option>--help</option></term>
-
- <xi:include href="standard-options.xml" xpointer="help-text" />
- </varlistentry>
+ <xi:include href="standard-options.xml" xpointer="help" />
<xi:include href="standard-options.xml" xpointer="version" />
</variablelist>
</refsect1>
<listitem><para>Takes a boolean argument. If true, explicit module loading will be denied. This allows to turn
off module load and unload operations on modular kernels. It is recommended to turn this on for most services
- that do not need special file systems or extra kernel modules to work. Default to off. Enabling this option
+ that do not need special file systems or extra kernel modules to work. Defaults to off. Enabling this option
removes <constant>CAP_SYS_MODULE</constant> from the capability bounding set for the unit, and installs a
system call filter to block module system calls, also <filename>/usr/lib/modules</filename> is made
inaccessible. For this setting the same restrictions regarding mount propagation and privileges apply as for
<term><varname>VLANId=</varname></term>
<listitem>
<para>The VLAN ID for the new static MAC table entry. If
- omitted, no VLAN ID info is appended to the new static MAC
+ omitted, no VLAN ID information is appended to the new static MAC
table entry.</para>
</listitem>
</varlistentry>
along with systemd; If not, see <http://www.gnu.org/licenses/>.
-->
-<refentry id="udevadm">
+<refentry id="udevadm"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
+
<refentryinfo>
<title>udevadm</title>
<productname>systemd</productname>
<arg><option>--help</option></arg>
</cmdsynopsis>
<cmdsynopsis>
- <command>udevadm info <replaceable>options</replaceable></command>
+ <command>udevadm info <optional>options</optional> <optional>devpath</optional></command>
</cmdsynopsis>
<cmdsynopsis>
- <command>udevadm trigger <optional>options</optional></command>
+ <command>udevadm trigger <optional>options</optional> <optional>devpath</optional></command>
</cmdsynopsis>
<cmdsynopsis>
<command>udevadm settle <optional>options</optional></command>
</cmdsynopsis>
<cmdsynopsis>
- <command>udevadm control <replaceable>command</replaceable></command>
+ <command>udevadm control <replaceable>option</replaceable></command>
</cmdsynopsis>
<cmdsynopsis>
<command>udevadm monitor <optional>options</optional></command>
<refsect1><title>Options</title>
<variablelist>
<varlistentry>
+ <term><option>-d</option></term>
<term><option>--debug</option></term>
<listitem>
<para>Print debug messages to standard error.</para>
</listitem>
</varlistentry>
- <varlistentry>
- <term><option>--version</option></term>
- <listitem>
- <para>Print version number.</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><option>-h</option></term>
- <term><option>--help</option></term>
- <listitem>
- <para>Print help text.</para>
- </listitem>
- </varlistentry>
+
+ <xi:include href="standard-options.xml" xpointer="version" />
+ <xi:include href="standard-options.xml" xpointer="help" />
</variablelist>
<refsect2><title>udevadm info
<para>Cleanup the udev database.</para>
</listitem>
</varlistentry>
- <varlistentry>
- <term><option>--version</option></term>
- <listitem>
- <para>Print version.</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><option>-h</option></term>
- <term><option>--help</option></term>
- <listitem>
- <para>Print help text.</para>
- </listitem>
- </varlistentry>
+
+ <xi:include href="standard-options.xml" xpointer="version" />
+ <xi:include href="standard-options.xml" xpointer="help" />
</variablelist>
<para>In addition, an optional positional argument can be used
device.</para>
</listitem>
</varlistentry>
- <varlistentry>
- <term><option>-h</option></term>
- <term><option>--help</option></term>
- <listitem>
- <para>Print help text.</para>
- </listitem>
- </varlistentry>
+
+ <xi:include href="standard-options.xml" xpointer="version" />
+ <xi:include href="standard-options.xml" xpointer="help" />
</variablelist>
<para>In addition, optional positional arguments can be used
<para>Stop waiting if file exists.</para>
</listitem>
</varlistentry>
- <varlistentry>
- <term><option>-h</option></term>
- <term><option>--help</option></term>
- <listitem>
- <para>Print help text.</para>
- </listitem>
- </varlistentry>
+
+ <xi:include href="standard-options.xml" xpointer="version" />
+ <xi:include href="standard-options.xml" xpointer="help" />
</variablelist>
</refsect2>
- <refsect2><title>udevadm control <replaceable>command</replaceable></title>
+ <refsect2><title>udevadm control <replaceable>option</replaceable></title>
<para>Modify the internal state of the running udev daemon.</para>
<variablelist>
<varlistentry>
</listitem>
</varlistentry>
<varlistentry>
+ <term><option>-t</option></term>
<term><option>--timeout=</option><replaceable>seconds</replaceable></term>
<listitem>
<para>The maximum number of seconds to wait for a reply from systemd-udevd.</para>
</listitem>
</varlistentry>
- <varlistentry>
- <term><option>-h</option></term>
- <term><option>--help</option></term>
- <listitem>
- <para>Print help text.</para>
- </listitem>
- </varlistentry>
+
+ <xi:include href="standard-options.xml" xpointer="version" />
+ <xi:include href="standard-options.xml" xpointer="help" />
</variablelist>
</refsect2>
<para>Filter udev events by tag. Only udev events with a given tag attached will pass.</para>
</listitem>
</varlistentry>
- <varlistentry>
- <term><option>-h</option></term>
- <term><option>--help</option></term>
- <listitem>
- <para>Print help text.</para>
- </listitem>
- </varlistentry>
+
+ <xi:include href="standard-options.xml" xpointer="version" />
+ <xi:include href="standard-options.xml" xpointer="help" />
</variablelist>
</refsect2>
and all devices will be owned by root.</para>
</listitem>
</varlistentry>
- <varlistentry>
- <term><option>-h</option></term>
- <term><option>--help</option></term>
- <listitem>
- <para>Print help text.</para>
- </listitem>
- </varlistentry>
+
+ <xi:include href="standard-options.xml" xpointer="version" />
+ <xi:include href="standard-options.xml" xpointer="help" />
</variablelist>
</refsect2>
for device <replaceable>DEVPATH</replaceable>, and print debug
output.</para>
<variablelist>
- <varlistentry>
- <term><option>-h</option></term>
- <term><option>--help</option></term>
- <listitem>
- <para>Print help text.</para>
- </listitem>
- </varlistentry>
+ <xi:include href="standard-options.xml" xpointer="version" />
+ <xi:include href="standard-options.xml" xpointer="help" />
</variablelist>
</refsect2>
</refsect1>
conf.set('TTY_GID', tty_gid)
substs.set('TTY_GID', tty_gid)
+# Ensure provided GID argument is numeric, otherwise fallback to default assignment
+if get_option('users-gid') != ''
+ users_gid = get_option('users-gid').to_int()
+else
+ users_gid = '-'
+endif
+substs.set('USERS_GID', users_gid)
+
if get_option('adm-group')
m4_defines += ['-DENABLE_ADM_GROUP']
endif
'debug shell: @0@ @ @1@'.format(get_option('debug-shell'),
get_option('debug-tty')),
'TTY GID: @0@'.format(tty_gid),
+ 'users GID: @0@'.format(users_gid),
'maximum system UID: @0@'.format(system_uid_max),
'maximum system GID: @0@'.format(system_gid_max),
'/dev/kvm access mode: @0@'.format(get_option('dev-kvm-mode')),
option('tty-gid', type : 'string',
description : 'the numeric GID of the "tty" group',
value : '5')
+option('users-gid', type : 'string',
+ description : 'the numeric GID of the "users" group')
option('adm-group', type : 'boolean',
description : 'the ACL for adm group should be added')
option('wheel-group', type : 'boolean',
size_t size;
char *ptr;
int r;
+ usec_t activated_time = USEC_INFINITY;
+ _cleanup_free_ char* path = NULL, *unit_id = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
r = acquire_boot_times(bus, &t);
if (r < 0)
return r;
+ path = unit_dbus_path_from_name(SPECIAL_DEFAULT_TARGET);
+ if (!path)
+ return log_oom();
+
+ r = sd_bus_get_property_string(
+ bus,
+ "org.freedesktop.systemd1",
+ path,
+ "org.freedesktop.systemd1.Unit",
+ "Id",
+ &error,
+ &unit_id);
+ if (r < 0) {
+ log_error_errno(r, "default.target doesn't seem to exist: %s", bus_error_message(&error, r));
+ unit_id = NULL;
+ }
+
+ r = bus_get_uint64_property(bus, path,
+ "org.freedesktop.systemd1.Unit",
+ "ActiveEnterTimestampMonotonic",
+ &activated_time);
+ if (r < 0) {
+ log_info_errno(r, "default.target seems not to be started. Continuing...");
+ activated_time = USEC_INFINITY;
+ }
+
ptr = buf;
size = sizeof(buf);
size = strpcpyf(&ptr, size, "%s (userspace) ", format_timespan(ts, sizeof(ts), t->finish_time - t->userspace_time, USEC_PER_MSEC));
strpcpyf(&ptr, size, "= %s", format_timespan(ts, sizeof(ts), t->firmware_time + t->finish_time, USEC_PER_MSEC));
+ if (unit_id && activated_time != USEC_INFINITY)
+ size = strpcpyf(&ptr, size, "\n%s reached after %s in userspace", unit_id, format_timespan(ts, sizeof(ts), activated_time - t->userspace_time, USEC_PER_MSEC));
+
ptr = strdup(buf);
if (!ptr)
return log_oom();
* Suggested usage: whenever you want to canonicalize a path, use this function. Pass the absolute path you got
* as-is: fully qualified and relative to your host's root. Optionally, specify the root parameter to tell this
* function what to do when encountering a symlink with an absolute path as directory: prefix it by the
- * specified path.
- *
- * Note: there's also chase_symlinks_prefix() (see below), which as first step prefixes the passed path by the
- * passed root. */
+ * specified path. */
if (original_root) {
r = path_make_absolute_cwd(original_root, &root);
* what we got so far. But don't allow this if the remaining path contains "../ or "./"
* or something else weird. */
+ /* If done is "/", as first also contains slash at the head, then remove this redundant slash. */
+ if (streq_ptr(done, "/"))
+ *done = '\0';
+
if (!strextend(&done, first, todo, NULL))
return -ENOMEM;
done = first;
first = NULL;
} else {
+ /* If done is "/", as first also contains slash at the head, then remove this redundant slash. */
+ if (streq(done, "/"))
+ *done = '\0';
+
if (!strextend(&done, first, NULL))
return -ENOMEM;
}
return base64_append_width(prefix, plen, NULL, plen, p, l, width - plen - 1);
}
-int unbase64mem(const char *p, size_t l, void **mem, size_t *_len) {
- _cleanup_free_ uint8_t *r = NULL;
- int a, b, c, d;
- uint8_t *z;
+static int unbase64_next(const char **p, size_t *l) {
+ int ret;
+
+ assert(p);
+ assert(l);
+
+ /* Find the next non-whitespace character, and decode it. If we find padding, we return it as INT_MAX. We
+ * greedily skip all preceeding and all following whitespace. */
+
+ for (;;) {
+ if (*l == 0)
+ return -EPIPE;
+
+ if (!strchr(WHITESPACE, **p))
+ break;
+
+ /* Skip leading whitespace */
+ (*p)++, (*l)--;
+ }
+
+ if (**p == '=')
+ ret = INT_MAX; /* return padding as INT_MAX */
+ else {
+ ret = unbase64char(**p);
+ if (ret < 0)
+ return ret;
+ }
+
+ for (;;) {
+ (*p)++, (*l)--;
+
+ if (*l == 0)
+ break;
+ if (!strchr(WHITESPACE, **p))
+ break;
+
+ /* Skip following whitespace */
+ }
+
+ return ret;
+}
+
+int unbase64mem(const char *p, size_t l, void **ret, size_t *ret_size) {
+ _cleanup_free_ uint8_t *buf = NULL;
const char *x;
+ uint8_t *z;
size_t len;
assert(p || l == 0);
- assert(mem);
- assert(_len);
+ assert(ret);
+ assert(ret_size);
if (l == (size_t) -1)
l = strlen(p);
- /* padding ensures any base63 input has input divisible by 4 */
- if (l % 4 != 0)
- return -EINVAL;
-
- /* strip the padding */
- if (l > 0 && p[l - 1] == '=')
- l--;
- if (l > 0 && p[l - 1] == '=')
- l--;
-
- /* a group of four input bytes needs three output bytes, in case of
- padding we need to add two or three extra bytes */
- len = (l / 4) * 3 + (l % 4 ? (l % 4) - 1 : 0);
+ /* A group of four input bytes needs three output bytes, in case of padding we need to add two or three extra
+ bytes. Note that this calculation is an upper boundary, as we ignore whitespace while decoding */
+ len = (l / 4) * 3 + (l % 4 != 0 ? (l % 4) - 1 : 0);
- z = r = malloc(len + 1);
- if (!r)
+ buf = malloc(len + 1);
+ if (!buf)
return -ENOMEM;
- for (x = p; x < p + (l / 4) * 4; x += 4) {
- /* a == 00XXXXXX; b == 00YYYYYY; c == 00ZZZZZZ; d == 00WWWWWW */
- a = unbase64char(x[0]);
+ for (x = p, z = buf;;) {
+ int a, b, c, d; /* a == 00XXXXXX; b == 00YYYYYY; c == 00ZZZZZZ; d == 00WWWWWW */
+
+ a = unbase64_next(&x, &l);
+ if (a == -EPIPE) /* End of string */
+ break;
if (a < 0)
+ return a;
+ if (a == INT_MAX) /* Padding is not allowed at the beginning of a 4ch block */
return -EINVAL;
- b = unbase64char(x[1]);
+ b = unbase64_next(&x, &l);
if (b < 0)
+ return b;
+ if (b == INT_MAX) /* Padding is not allowed at the second character of a 4ch block either */
return -EINVAL;
- c = unbase64char(x[2]);
+ c = unbase64_next(&x, &l);
if (c < 0)
- return -EINVAL;
+ return c;
- d = unbase64char(x[3]);
+ d = unbase64_next(&x, &l);
if (d < 0)
- return -EINVAL;
-
- *(z++) = (uint8_t) a << 2 | (uint8_t) b >> 4; /* XXXXXXYY */
- *(z++) = (uint8_t) b << 4 | (uint8_t) c >> 2; /* YYYYZZZZ */
- *(z++) = (uint8_t) c << 6 | (uint8_t) d; /* ZZWWWWWW */
- }
-
- switch (l % 4) {
+ return d;
- case 3:
- a = unbase64char(x[0]);
- if (a < 0)
- return -EINVAL;
+ if (c == INT_MAX) { /* Padding at the third character */
- b = unbase64char(x[1]);
- if (b < 0)
- return -EINVAL;
+ if (d != INT_MAX) /* If the third character is padding, the fourth must be too */
+ return -EINVAL;
- c = unbase64char(x[2]);
- if (c < 0)
- return -EINVAL;
+ /* b == 00YY0000 */
+ if (b & 15)
+ return -EINVAL;
- /* c == 00ZZZZ00 */
- if (c & 3)
- return -EINVAL;
-
- *(z++) = (uint8_t) a << 2 | (uint8_t) b >> 4; /* XXXXXXYY */
- *(z++) = (uint8_t) b << 4 | (uint8_t) c >> 2; /* YYYYZZZZ */
-
- break;
- case 2:
- a = unbase64char(x[0]);
- if (a < 0)
- return -EINVAL;
+ if (l > 0) /* Trailing rubbish? */
+ return -ENAMETOOLONG;
- b = unbase64char(x[1]);
- if (b < 0)
- return -EINVAL;
+ *(z++) = (uint8_t) a << 2 | (uint8_t) (b >> 4); /* XXXXXXYY */
+ break;
+ }
- /* b == 00YY0000 */
- if (b & 15)
- return -EINVAL;
+ if (d == INT_MAX) {
+ /* c == 00ZZZZ00 */
+ if (c & 3)
+ return -EINVAL;
- *(z++) = (uint8_t) a << 2 | (uint8_t) (b >> 4); /* XXXXXXYY */
+ if (l > 0) /* Trailing rubbish? */
+ return -ENAMETOOLONG;
- break;
- case 0:
+ *(z++) = (uint8_t) a << 2 | (uint8_t) b >> 4; /* XXXXXXYY */
+ *(z++) = (uint8_t) b << 4 | (uint8_t) c >> 2; /* YYYYZZZZ */
+ break;
+ }
- break;
- default:
- return -EINVAL;
+ *(z++) = (uint8_t) a << 2 | (uint8_t) b >> 4; /* XXXXXXYY */
+ *(z++) = (uint8_t) b << 4 | (uint8_t) c >> 2; /* YYYYZZZZ */
+ *(z++) = (uint8_t) c << 6 | (uint8_t) d; /* ZZWWWWWW */
}
*z = 0;
- *mem = r;
- r = NULL;
- *_len = len;
+ if (ret_size)
+ *ret_size = (size_t) (z - buf);
+
+ *ret = buf;
+ buf = NULL;
return 0;
}
#define PR_SET_CHILD_SUBREAPER 36
#endif
-#ifndef MAX_HANDLE_SZ
-#define MAX_HANDLE_SZ 128
-#endif
-
#if ! HAVE_SECURE_GETENV
# if HAVE___SECURE_GETENV
# define secure_getenv __secure_getenv
#include "string-util.h"
#include "strv.h"
+/* This is the original MAX_HANDLE_SZ definition from the kernel, when the API was introduced. We use that in place of
+ * any more currently defined value to future-proof things: if the size is increased in the API headers, and our code
+ * is recompiled then it would cease working on old kernels, as those refuse any sizes larger than this value with
+ * EINVAL right-away. Hence, let's disconnect ourselves from any such API changes, and stick to the original definition
+ * from when it was introduced. We use it as a start value only anyway (see below), and hence should be able to deal
+ * with large file handles anyway. */
+#define ORIGINAL_MAX_HANDLE_SZ 128
+
int name_to_handle_at_loop(
int fd,
const char *path,
int flags) {
_cleanup_free_ struct file_handle *h;
- size_t n = MAX_HANDLE_SZ;
+ size_t n = ORIGINAL_MAX_HANDLE_SZ;
/* We need to invoke name_to_handle_at() in a loop, given that it might return EOVERFLOW when the specified
* buffer is too small. Note that in contrast to what the docs might suggest, MAX_HANDLE_SZ is only good as a
"xfs");
}
+bool fstype_can_uid_gid(const char *fstype) {
+
+ /* All file systems that have a uid=/gid= mount option that fixates the owners of all files and directories,
+ * current and future. */
+
+ return STR_IN_SET(fstype,
+ "adfs",
+ "fat",
+ "hfs",
+ "hpfs",
+ "iso9660",
+ "msdos",
+ "ntfs",
+ "vfat");
+}
+
int repeat_unmount(const char *path, int flags) {
bool done = false;
bool fstype_is_api_vfs(const char *fstype);
bool fstype_is_ro(const char *fsype);
bool fstype_can_discard(const char *fstype);
+bool fstype_can_uid_gid(const char *fstype);
const char* mode_to_inaccessible_node(mode_t mode);
* for Gentoo which does a merge without making /lib a symlink.
*/
"lib/systemd/libsystemd-shared-*.so\0"
- "usr/lib/systemd/libsystemd-shared-*.so\0") {
+ "lib64/systemd/libsystemd-shared-*.so\0"
+ "usr/lib/systemd/libsystemd-shared-*.so\0"
+ "usr/lib64/systemd/libsystemd-shared-*.so\0") {
_cleanup_strv_free_ char **names = NULL;
_cleanup_free_ char *path = NULL;
return obuf;
}
-char *strextend(char **x, ...) {
- va_list ap;
- size_t f, l;
+char *strextend_with_separator(char **x, const char *separator, ...) {
+ bool need_separator;
+ size_t f, l, l_separator;
char *r, *p;
+ va_list ap;
assert(x);
l = f = strlen_ptr(*x);
- va_start(ap, x);
+ need_separator = !isempty(*x);
+ l_separator = strlen_ptr(separator);
+
+ va_start(ap, separator);
for (;;) {
const char *t;
size_t n;
break;
n = strlen(t);
+
+ if (need_separator)
+ n += l_separator;
+
if (n > ((size_t) -1) - l) {
va_end(ap);
return NULL;
}
l += n;
+ need_separator = true;
}
va_end(ap);
+ need_separator = !isempty(*x);
+
r = realloc(*x, l+1);
if (!r)
return NULL;
p = r + f;
- va_start(ap, x);
+ va_start(ap, separator);
for (;;) {
const char *t;
if (!t)
break;
+ if (need_separator && separator)
+ p = stpcpy(p, separator);
+
p = stpcpy(p, t);
+
+ need_separator = true;
}
va_end(ap);
+ assert(p == r + l);
+
*p = 0;
*x = r;
char *strip_tab_ansi(char **p, size_t *l);
-char *strextend(char **x, ...) _sentinel_;
+char *strextend_with_separator(char **x, const char *separator, ...) _sentinel_;
+
+#define strextend(x, ...) strextend_with_separator(x, NULL, __VA_ARGS__)
char *strrep(const char *s, unsigned n);
" --match=MATCH Only show matching messages\n"
" --size=SIZE Maximum length of captured packet\n"
" --list Don't show tree, but simple object path list\n"
- " --quiet Don't show method call reply\n"
+ " -q --quiet Don't show method call reply\n"
" --verbose Show result values in long format\n"
" --expect-reply=BOOL Expect a method call reply\n"
" --auto-start=BOOL Auto-start destination service\n"
return r;
}
+#if ENABLE_SMACK
static int setup_smack(
const ExecContext *context,
const ExecCommand *command) {
return 0;
}
+#endif
static int compile_bind_mounts(
const ExecContext *context,
int *exit_status) {
_cleanup_strv_free_ char **our_env = NULL, **pass_env = NULL, **accum_env = NULL, **final_argv = NULL;
- _cleanup_free_ char *mac_selinux_context_net = NULL, *home_buffer = NULL;
+ _cleanup_free_ char *home_buffer = NULL;
_cleanup_free_ gid_t *supplementary_gids = NULL;
const char *username = NULL, *groupname = NULL;
const char *home = NULL, *shell = NULL;
needs_mount_namespace, /* Do we need to set up a mount namespace for this kernel? */
needs_ambient_hack; /* Do we need to apply the ambient capabilities hack? */
#if HAVE_SELINUX
+ _cleanup_free_ char *mac_selinux_context_net = NULL;
bool use_selinux = false;
#endif
#if ENABLE_SMACK
void *data,
void *userdata) {
- _cleanup_free_ char *cleaned = NULL;
_cleanup_free_ void *p = NULL;
ExecContext *c = data;
size_t sz;
return 0;
}
- /* Be tolerant to whitespace. Remove it all before decoding */
- cleaned = strdup(rvalue);
- if (!cleaned)
- return log_oom();
- delete_chars(cleaned, WHITESPACE);
-
- r = unbase64mem(cleaned, (size_t) -1, &p, &sz);
+ r = unbase64mem(rvalue, (size_t) -1, &p, &sz);
if (r < 0)
- return log_syntax(unit, LOG_ERR, filename, line, r, "Failed to decode base64 data, ignoring: %s", cleaned);
+ return log_syntax(unit, LOG_ERR, filename, line, r, "Failed to decode base64 data, ignoring: %s", rvalue);
assert(sz > 0);
if (root_image) {
/* A root image is specified, mount it to the right place */
- r = dissected_image_mount(dissected_image, root, dissect_image_flags);
+ r = dissected_image_mount(dissected_image, root, UID_INVALID, dissect_image_flags);
if (r < 0)
goto finish;
}
}
-static bool service_will_restart(Service *s) {
+static bool service_will_restart(Unit *u) {
+ Service *s = SERVICE(u);
+
assert(s);
+ if (s->will_auto_restart)
+ return true;
if (s->state == SERVICE_AUTO_RESTART)
return true;
if (!UNIT(s)->job)
if (s->result != SERVICE_SUCCESS)
log_unit_warning(UNIT(s), "Failed with result '%s'.", service_result_to_string(s->result));
+ if (allow_restart && service_shall_restart(s))
+ s->will_auto_restart = true;
+
/* Make sure service_release_resources() doesn't destroy our FD store, while we are changing through
* SERVICE_FAILED/SERVICE_DEAD before entering into SERVICE_AUTO_RESTART. */
s->n_keep_fd_store ++;
service_set_state(s, s->result != SERVICE_SUCCESS ? SERVICE_FAILED : SERVICE_DEAD);
- if (allow_restart && service_shall_restart(s)) {
+ if (s->will_auto_restart) {
+ s->will_auto_restart = false;
r = service_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), s->restart_usec));
if (r < 0) {
s->exec_runtime = exec_runtime_unref(s->exec_runtime);
if (s->exec_context.runtime_directory_preserve_mode == EXEC_PRESERVE_NO ||
- (s->exec_context.runtime_directory_preserve_mode == EXEC_PRESERVE_RESTART && !service_will_restart(s)))
+ (s->exec_context.runtime_directory_preserve_mode == EXEC_PRESERVE_RESTART && !service_will_restart(UNIT(s))))
/* Also, remove the runtime directory */
exec_context_destroy_runtime_directory(&s->exec_context, UNIT(s)->manager->prefix[EXEC_DIRECTORY_RUNTIME]);
.active_state = service_active_state,
.sub_state_to_string = service_sub_state_to_string,
+ .will_restart = service_will_restart,
+
.check_gc = service_check_gc,
.sigchld_event = service_sigchld_event,
bool main_pid_alien:1;
bool bus_name_good:1;
bool forbid_restart:1;
+ /* Keep restart intention between UNIT_FAILED and UNIT_ACTIVATING */
+ bool will_auto_restart:1;
bool start_timeout_defined:1;
char *bus_name;
void *v;
HASHMAP_FOREACH_KEY(v, other, u->dependencies[needed_dependencies[j]], i)
- if (unit_active_or_pending(other))
+ if (unit_active_or_pending(other) || unit_will_restart(other))
return;
}
return false;
}
+bool unit_will_restart(Unit *u) {
+ assert(u);
+
+ if (!UNIT_VTABLE(u)->will_restart)
+ return false;
+
+ return UNIT_VTABLE(u)->will_restart(u);
+}
+
int unit_kill(Unit *u, KillWho w, int signo, sd_bus_error *error) {
assert(u);
assert(w >= 0 && w < _KILL_WHO_MAX);
* unit is in. */
const char* (*sub_state_to_string)(Unit *u);
+ /* Additionally to UnitActiveState determine whether unit is to be restarted. */
+ bool (*will_restart)(Unit *u);
+
/* Return true when there is reason to keep this entry around
* even nothing references it and it isn't active in any
* way */
bool unit_stop_pending(Unit *u) _pure_;
bool unit_inactive_or_pending(Unit *u) _pure_;
bool unit_active_or_pending(Unit *u);
+bool unit_will_restart(Unit *u);
int unit_add_default_target_dependency(Unit *u, Unit *target);
#include "loop-util.h"
#include "string-util.h"
#include "strv.h"
+#include "user-util.h"
#include "util.h"
static enum {
if (r < 0)
goto finish;
- r = dissected_image_mount(m, arg_path, arg_flags);
+ r = dissected_image_mount(m, arg_path, UID_INVALID, arg_flags);
if (r < 0) {
log_error_errno(r, "Failed to mount image: %m");
goto finish;
return log_oom();
} else if (streq(key, "rootflags")) {
- char *o;
if (proc_cmdline_value_missing(key, value))
return 0;
- o = arg_root_options ?
- strjoin(arg_root_options, ",", value) :
- strdup(value);
- if (!o)
+ if (!strextend_with_separator(&arg_root_options, ",", value, NULL))
return log_oom();
- free(arg_root_options);
- arg_root_options = o;
} else if (streq(key, "roothash")) {
if (proc_cmdline_value_missing(key, value))
return log_oom();
} else if (streq(key, "mount.usrflags")) {
- char *o;
if (proc_cmdline_value_missing(key, value))
return 0;
- o = arg_usr_options ?
- strjoin(arg_usr_options, ",", value) :
- strdup(value);
- if (!o)
+ if (!strextend_with_separator(&arg_usr_options, ",", value, NULL))
return log_oom();
- free(arg_usr_options);
- arg_usr_options = o;
-
} else if (streq(key, "rw") && !value)
arg_root_rw = true;
else if (streq(key, "ro") && !value)
" --follow[=BOOL] Do [not] wait for input\n"
" --save-state[=FILE] Save uploaded cursors (default \n"
" " STATE_FILE ")\n"
- " -h --help Show this help and exit\n"
- " --version Print version string and exit\n"
, program_invocation_short_name);
}
#include "escape.h"
#include "fd-util.h"
#include "fileio.h"
+#include "fs-util.h"
#include "fstab-util.h"
#include "mount-util.h"
#include "pager.h"
return log_oom();
} else if (arg_transport == BUS_TRANSPORT_LOCAL) {
- _cleanup_free_ char *u = NULL, *p = NULL;
+ _cleanup_free_ char *u = NULL;
u = fstab_node_to_udev_node(argv[optind]);
if (!u)
return log_oom();
- r = path_make_absolute_cwd(u, &p);
+ r = chase_symlinks(u, NULL, 0, &arg_mount_what);
if (r < 0)
return log_error_errno(r, "Failed to make path %s absolute: %m", u);
-
- arg_mount_what = canonicalize_file_name(p);
- if (!arg_mount_what)
- return log_error_errno(errno, "Failed to canonicalize path %s: %m", p);
} else {
arg_mount_what = strdup(argv[optind]);
if (!arg_mount_what)
if (argc > optind+1) {
if (arg_transport == BUS_TRANSPORT_LOCAL) {
- _cleanup_free_ char *p = NULL;
-
- r = path_make_absolute_cwd(argv[optind+1], &p);
+ r = chase_symlinks(argv[optind+1], NULL, CHASE_NONEXISTENT, &arg_mount_where);
if (r < 0)
return log_error_errno(r, "Failed to make path %s absolute: %m", argv[optind+1]);
-
- arg_mount_where = canonicalize_file_name(p);
- if (!arg_mount_where)
- return log_error_errno(errno, "Failed to canonicalize path %s: %m", p);
-
} else {
arg_mount_where = strdup(argv[optind+1]);
if (!arg_mount_where)
r = unit_name_from_path(where, suffix, &mount_unit);
if (r < 0)
- return log_error_errno(r, "Failed to make mount unit name from path %s: %m", where);
+ return log_error_errno(r, "Failed to make %s unit name from path %s: %m", suffix + 1, where);
r = sd_bus_message_new_method_call(
bus,
polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
r = sd_bus_call(bus, m, 0, &error, &reply);
- if (r < 0)
- return log_error_errno(r, "Failed to stop mount unit: %s", bus_error_message(&error, r));
+ if (r < 0) {
+ if (streq(suffix, ".automount") &&
+ sd_bus_error_has_name(&error, "org.freedesktop.systemd1.NoSuchUnit"))
+ return 0;
+ return log_error_errno(r, "Failed to stop %s unit: %s", suffix + 1, bus_error_message(&error, r));
+ }
if (w) {
const char *object;
}
for (i = optind; i < argc; i++) {
- _cleanup_free_ char *u = NULL, *a = NULL, *p = NULL;
+ _cleanup_free_ char *u = NULL, *p = NULL;
struct stat st;
u = fstab_node_to_udev_node(argv[i]);
if (!u)
return log_oom();
- r = path_make_absolute_cwd(u, &a);
+ r = chase_symlinks(u, NULL, 0, &p);
if (r < 0) {
r2 = log_error_errno(r, "Failed to make path %s absolute: %m", argv[i]);
continue;
}
- p = canonicalize_file_name(a);
-
- if (!p) {
- r2 = log_error_errno(errno, "Failed to canonicalize path %s: %m", argv[i]);
- continue;
- }
-
if (stat(p, &st) < 0)
return log_error_errno(errno, "Can't stat %s (from %s): %m", p, argv[i]);
FOREACH_STRING(fn,
".",
- "tasks",
- "notify_on_release",
- "cgroup.procs",
- "cgroup.events",
"cgroup.clone_children",
"cgroup.controllers",
- "cgroup.subtree_control")
+ "cgroup.events",
+ "cgroup.procs",
+ "cgroup.stat",
+ "cgroup.subtree_control",
+ "cgroup.threads",
+ "notify_on_release",
+ "tasks")
if (fchownat(fd, fn, uid_shift, uid_shift, 0) < 0)
log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_WARNING, errno,
"Failed to chown \"%s/%s\", ignoring: %m", path, fn);
return 0;
}
-int chown_cgroup(pid_t pid, uid_t uid_shift) {
+int chown_cgroup(pid_t pid, CGroupUnified unified_requested, uid_t uid_shift) {
_cleanup_free_ char *path = NULL, *fs = NULL;
int r;
if (r < 0)
return log_error_errno(r, "Failed to chown() cgroup %s: %m", fs);
+ if (unified_requested == CGROUP_UNIFIED_SYSTEMD) {
+ _cleanup_free_ char *lfs = NULL;
+ /* Always propagate access rights from unified to legacy controller */
+
+ r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER_LEGACY, path, NULL, &lfs);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get file system path for container cgroup: %m");
+
+ r = chown_cgroup_path(lfs, uid_shift);
+ if (r < 0)
+ return log_error_errno(r, "Failed to chown() cgroup %s: %m", lfs);
+ }
+
return 0;
}
#include "cgroup-util.h"
-int chown_cgroup(pid_t pid, uid_t uid_shift);
+int chown_cgroup(pid_t pid, CGroupUnified unified_requested, uid_t uid_shift);
int sync_cgroup(pid_t pid, CGroupUnified unified_requested, uid_t uid_shift);
int create_subcgroup(pid_t pid, CGroupUnified unified_requested);
return 0;
}
-static int detect_unified_cgroup_hierarchy(const char *directory) {
+static int detect_unified_cgroup_hierarchy_from_environment(void) {
const char *e;
int r;
arg_unified_cgroup_hierarchy = CGROUP_UNIFIED_ALL;
else
arg_unified_cgroup_hierarchy = CGROUP_UNIFIED_NONE;
-
- return 0;
}
- /* Otherwise inherit the default from the host system */
+ return 0;
+}
+
+static int detect_unified_cgroup_hierarchy_from_image(const char *directory) {
+ int r;
+
+ /* Let's inherit the mode to use from the host system, but let's take into consideration what systemd in the
+ * image actually supports. */
r = cg_all_unified();
if (r < 0)
return log_error_errno(r, "Failed to determine whether we are in all unified mode.");
} else
arg_unified_cgroup_hierarchy = CGROUP_UNIFIED_NONE;
+ log_debug("Using %s hierarchy for container.",
+ arg_unified_cgroup_hierarchy == CGROUP_UNIFIED_NONE ? "legacy" :
+ arg_unified_cgroup_hierarchy == CGROUP_UNIFIED_SYSTEMD ? "hybrid" : "unified");
+
return 0;
}
int kmsg_socket,
int rtnl_socket,
int uid_shift_socket,
+ int unified_cgroup_hierarchy_socket,
FDSet *fds) {
pid_t pid;
return r;
if (dissected_image) {
- r = dissected_image_mount(dissected_image, directory, DISSECT_IMAGE_DISCARD_ON_LOOP|(arg_read_only ? DISSECT_IMAGE_READ_ONLY : 0));
+ /* If we are operating on a disk image, then mount its root directory now, but leave out the rest. We
+ * can read the UID shift from it if we need to. Further down we'll mount the rest, but then with the
+ * uid shift known. That way we can mount VFAT file systems shifted to the right place right away. This
+ * makes sure ESP partitions and userns are compatible. */
+
+ r = dissected_image_mount(dissected_image, directory, arg_uid_shift,
+ DISSECT_IMAGE_MOUNT_ROOT_ONLY|DISSECT_IMAGE_DISCARD_ON_LOOP|(arg_read_only ? DISSECT_IMAGE_READ_ONLY : 0));
if (r < 0)
return r;
}
log_info("Selected user namespace base " UID_FMT " and range " UID_FMT ".", arg_uid_shift, arg_uid_range);
}
+ if (dissected_image) {
+ /* Now we know the uid shift, let's now mount everything else that might be in the image. */
+ r = dissected_image_mount(dissected_image, directory, arg_uid_shift,
+ DISSECT_IMAGE_MOUNT_NON_ROOT_ONLY|DISSECT_IMAGE_DISCARD_ON_LOOP|(arg_read_only ? DISSECT_IMAGE_READ_ONLY : 0));
+ if (r < 0)
+ return r;
+ }
+
+ if (arg_unified_cgroup_hierarchy == CGROUP_UNIFIED_UNKNOWN) {
+ /* OK, we don't know yet which cgroup mode to use yet. Let's figure it out, and tell the parent. */
+
+ r = detect_unified_cgroup_hierarchy_from_image(directory);
+ if (r < 0)
+ return r;
+
+ l = send(unified_cgroup_hierarchy_socket, &arg_unified_cgroup_hierarchy, sizeof(arg_unified_cgroup_hierarchy), MSG_NOSIGNAL);
+ if (l < 0)
+ return log_error_errno(errno, "Failed to send cgroup mode: %m");
+ if (l != sizeof(arg_unified_cgroup_hierarchy)) {
+ log_error("Short write while sending cgroup mode: %m");
+ return -EIO;
+ }
+
+ unified_cgroup_hierarchy_socket = safe_close(unified_cgroup_hierarchy_socket);
+ }
+
/* Turn directory into bind mount */
r = mount_verbose(LOG_ERR, directory, directory, NULL, MS_BIND|MS_REC, NULL);
if (r < 0)
pid_socket_pair[2] = { -1, -1 },
uuid_socket_pair[2] = { -1, -1 },
notify_socket_pair[2] = { -1, -1 },
- uid_shift_socket_pair[2] = { -1, -1 };
+ uid_shift_socket_pair[2] = { -1, -1 },
+ unified_cgroup_hierarchy_socket_pair[2] = { -1, -1};
+
_cleanup_close_ int notify_socket= -1;
_cleanup_(barrier_destroy) Barrier barrier = BARRIER_NULL;
_cleanup_(sd_event_source_unrefp) sd_event_source *notify_event_source = NULL;
if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, uid_shift_socket_pair) < 0)
return log_error_errno(errno, "Failed to create uid shift socket pair: %m");
+ if (arg_unified_cgroup_hierarchy == CGROUP_UNIFIED_UNKNOWN)
+ if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, unified_cgroup_hierarchy_socket_pair) < 0)
+ return log_error_errno(errno, "Failed to create unified cgroup socket pair: %m");
+
/* Child can be killed before execv(), so handle SIGCHLD in order to interrupt
* parent's blocking calls and give it a chance to call wait() and terminate. */
r = sigprocmask(SIG_UNBLOCK, &mask_chld, NULL);
uuid_socket_pair[0] = safe_close(uuid_socket_pair[0]);
notify_socket_pair[0] = safe_close(notify_socket_pair[0]);
uid_shift_socket_pair[0] = safe_close(uid_shift_socket_pair[0]);
+ unified_cgroup_hierarchy_socket_pair[0] = safe_close(unified_cgroup_hierarchy_socket_pair[0]);
(void) reset_all_signal_handlers();
(void) reset_signal_mask();
kmsg_socket_pair[1],
rtnl_socket_pair[1],
uid_shift_socket_pair[1],
+ unified_cgroup_hierarchy_socket_pair[1],
fds);
if (r < 0)
_exit(EXIT_FAILURE);
uuid_socket_pair[1] = safe_close(uuid_socket_pair[1]);
notify_socket_pair[1] = safe_close(notify_socket_pair[1]);
uid_shift_socket_pair[1] = safe_close(uid_shift_socket_pair[1]);
+ unified_cgroup_hierarchy_socket_pair[1] = safe_close(unified_cgroup_hierarchy_socket_pair[1]);
if (arg_userns_mode != USER_NAMESPACE_NO) {
/* The child just let us know the UID shift it might have read from the image. */
}
}
+ if (arg_unified_cgroup_hierarchy == CGROUP_UNIFIED_UNKNOWN) {
+ /* The child let us know the support cgroup mode it might have read from the image. */
+ l = recv(unified_cgroup_hierarchy_socket_pair[0], &arg_unified_cgroup_hierarchy, sizeof(arg_unified_cgroup_hierarchy), 0);
+ if (l < 0)
+ return log_error_errno(errno, "Failed to read cgroup mode: %m");
+ if (l != sizeof(arg_unified_cgroup_hierarchy)) {
+ log_error("Short read while reading cgroup mode.");
+ return -EIO;
+ }
+ }
+
/* Wait for the outer child. */
r = wait_for_terminate_and_warn("namespace helper", *pid, NULL);
if (r != 0)
return r;
}
- r = chown_cgroup(*pid, arg_uid_shift);
+ r = chown_cgroup(*pid, arg_unified_cgroup_hierarchy, arg_uid_shift);
if (r < 0)
return r;
if (r < 0)
goto finish;
+ r = detect_unified_cgroup_hierarchy_from_environment();
+ if (r < 0)
+ goto finish;
+
n_fd_passed = sd_listen_fds(false);
if (n_fd_passed > 0) {
r = fdset_new_listen_fds(&fds, false);
if (r < 0)
goto finish;
- r = detect_unified_cgroup_hierarchy(arg_directory);
- if (r < 0)
- goto finish;
-
interactive =
isatty(STDIN_FILENO) > 0 &&
isatty(STDOUT_FILENO) > 0;
return 0;
}
+#if HAVE_LIBCRYPTSETUP
static int resize_crypt_luks_device(dev_t devno, const char *fstype, dev_t main_devno) {
char devpath[DEV_NUM_PATH_MAX], main_devpath[DEV_NUM_PATH_MAX];
_cleanup_close_ int main_devfd = -1;
return 1;
}
+#endif
static int maybe_resize_slave_device(const char *mountpath, dev_t main_devno) {
dev_t devno;
_cleanup_free_ char *fstype = NULL;
int r;
+#if HAVE_LIBCRYPTSETUP
crypt_set_log_callback(NULL, cryptsetup_log_glue, NULL);
crypt_set_debug_level(1);
+#endif
r = get_block_device_harder(mountpath, &devno);
if (r < 0)
if (r < 0)
return log_warning_errno(r, "Failed to probe \"%s\": %m", devpath);
+#if HAVE_LIBCRYPTSETUP
if (streq_ptr(fstype, "crypto_LUKS"))
return resize_crypt_luks_device(devno, fstype, main_devno);
+#endif
log_debug("Don't know how to resize %s of type %s, ignoring", devpath, strnull(fstype));
return 0;
uint64_t *ret_pstart,
uint64_t *ret_psize,
sd_id128_t *ret_uuid) {
-
+#if HAVE_BLKID
_cleanup_blkid_free_probe_ blkid_probe b = NULL;
char t[DEV_NUM_PATH_MAX];
+ const char *v;
+#endif
uint64_t pstart = 0, psize = 0;
struct stat st, st2;
- const char *v, *t2;
+ const char *t2;
struct statfs sfs;
sd_id128_t uuid = SD_ID128_NULL;
uint32_t part = 0;
if (detect_container() > 0 || geteuid() != 0)
goto finish;
+#if HAVE_BLKID
xsprintf_dev_num_path(t, "block", st.st_dev);
errno = 0;
b = blkid_new_probe_from_filename(t);
r = safe_atou64(v, &psize);
if (r < 0)
return log_error_errno(r, "Failed to parse PART_ENTRY_SIZE field.");
+#endif
finish:
if (ret_part)
r = sd_bus_message_append(m, "v", "s", eq);
else if (streq(field, "StandardInputData")) {
- _cleanup_free_ char *cleaned = NULL;
_cleanup_free_ void *decoded = NULL;
size_t sz;
- cleaned = strdup(eq);
- if (!cleaned)
- return log_oom();
-
- delete_chars(cleaned, WHITESPACE);
-
- r = unbase64mem(cleaned, (size_t) -1, &decoded, &sz);
+ r = unbase64mem(eq, (size_t) -1, &decoded, &sz);
if (r < 0)
- return log_error_errno(r, "Failed to decode base64 data '%s': %m", cleaned);
+ return log_error_errno(r, "Failed to decode base64 data '%s': %m", eq);
r = sd_bus_message_open_container(m, 'v', "ay");
if (r < 0)
#include "string-util.h"
#include "strv.h"
#include "udev-util.h"
+#include "user-util.h"
#include "xattr-util.h"
int probe_filesystem(const char *node, char **ret_fstype) {
DissectedPartition *m,
const char *where,
const char *directory,
+ uid_t uid_shift,
DissectImageFlags flags) {
- const char *p, *options = NULL, *node, *fstype;
- _cleanup_free_ char *chased = NULL;
+ _cleanup_free_ char *chased = NULL, *options = NULL;
+ const char *p, *node, *fstype;
bool rw;
int r;
/* If requested, turn on discard support. */
if (fstype_can_discard(fstype) &&
((flags & DISSECT_IMAGE_DISCARD) ||
- ((flags & DISSECT_IMAGE_DISCARD_ON_LOOP) && is_loop_device(m->node))))
- options = "discard";
+ ((flags & DISSECT_IMAGE_DISCARD_ON_LOOP) && is_loop_device(m->node)))) {
+ options = strdup("discard");
+ if (!options)
+ return -ENOMEM;
+ }
+
+ if (uid_is_valid(uid_shift) && uid_shift != 0 && fstype_can_uid_gid(fstype)) {
+ _cleanup_free_ char *uid_option = NULL;
+
+ if (asprintf(&uid_option, "uid=" UID_FMT ",gid=" GID_FMT, uid_shift, (gid_t) uid_shift) < 0)
+ return -ENOMEM;
+
+ if (!strextend_with_separator(&options, ",", uid_option, NULL))
+ return -ENOMEM;
+ }
return mount_verbose(LOG_DEBUG, node, p, fstype, MS_NODEV|(rw ? 0 : MS_RDONLY), options);
}
-int dissected_image_mount(DissectedImage *m, const char *where, DissectImageFlags flags) {
+int dissected_image_mount(DissectedImage *m, const char *where, uid_t uid_shift, DissectImageFlags flags) {
int r;
assert(m);
if (!m->partitions[PARTITION_ROOT].found)
return -ENXIO;
- r = mount_partition(m->partitions + PARTITION_ROOT, where, NULL, flags);
- if (r < 0)
- return r;
+ if ((flags & DISSECT_IMAGE_MOUNT_NON_ROOT_ONLY) == 0) {
+ r = mount_partition(m->partitions + PARTITION_ROOT, where, NULL, uid_shift, flags);
+ if (r < 0)
+ return r;
+ }
- r = mount_partition(m->partitions + PARTITION_HOME, where, "/home", flags);
+ if ((flags & DISSECT_IMAGE_MOUNT_ROOT_ONLY))
+ return 0;
+
+ r = mount_partition(m->partitions + PARTITION_HOME, where, "/home", uid_shift, flags);
if (r < 0)
return r;
- r = mount_partition(m->partitions + PARTITION_SRV, where, "/srv", flags);
+ r = mount_partition(m->partitions + PARTITION_SRV, where, "/srv", uid_shift, flags);
if (r < 0)
return r;
r = dir_is_empty(p);
if (r > 0) {
- r = mount_partition(m->partitions + PARTITION_ESP, where, mp, flags);
+ r = mount_partition(m->partitions + PARTITION_ESP, where, mp, uid_shift, flags);
if (r < 0)
return r;
}
DissectImageFlags flags,
DecryptedImage **ret) {
- _cleanup_(decrypted_image_unrefp) DecryptedImage *d = NULL;
#if HAVE_LIBCRYPTSETUP
+ _cleanup_(decrypted_image_unrefp) DecryptedImage *d = NULL;
unsigned i;
int r;
#endif
if (mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL) < 0)
_exit(EXIT_FAILURE);
- r = dissected_image_mount(m, t, DISSECT_IMAGE_READ_ONLY);
+ r = dissected_image_mount(m, t, UID_INVALID, DISSECT_IMAGE_READ_ONLY);
if (r < 0)
_exit(EXIT_FAILURE);
}
typedef enum DissectImageFlags {
- DISSECT_IMAGE_READ_ONLY = 1,
- DISSECT_IMAGE_DISCARD_ON_LOOP = 2, /* Turn on "discard" if on a loop device and file system supports it */
- DISSECT_IMAGE_DISCARD = 4, /* Turn on "discard" if file system supports it, on all block devices */
- DISSECT_IMAGE_DISCARD_ON_CRYPTO = 8, /* Turn on "discard" also on crypto devices */
+ DISSECT_IMAGE_READ_ONLY = 1 << 0,
+ DISSECT_IMAGE_DISCARD_ON_LOOP = 1 << 1, /* Turn on "discard" if on a loop device and file system supports it */
+ DISSECT_IMAGE_DISCARD = 1 << 2, /* Turn on "discard" if file system supports it, on all block devices */
+ DISSECT_IMAGE_DISCARD_ON_CRYPTO = 1 << 3, /* Turn on "discard" also on crypto devices */
DISSECT_IMAGE_DISCARD_ANY = DISSECT_IMAGE_DISCARD_ON_LOOP |
DISSECT_IMAGE_DISCARD |
DISSECT_IMAGE_DISCARD_ON_CRYPTO,
- DISSECT_IMAGE_GPT_ONLY = 16, /* Only recognize images with GPT partition tables */
- DISSECT_IMAGE_REQUIRE_ROOT = 32, /* Don't accept disks without root partition */
+ DISSECT_IMAGE_GPT_ONLY = 1 << 4, /* Only recognize images with GPT partition tables */
+ DISSECT_IMAGE_REQUIRE_ROOT = 1 << 5, /* Don't accept disks without root partition */
+ DISSECT_IMAGE_MOUNT_ROOT_ONLY = 1 << 6, /* Mount only the root partition */
+ DISSECT_IMAGE_MOUNT_NON_ROOT_ONLY = 1 << 7, /* Mount only non-root partitions */
} DissectImageFlags;
struct DissectedImage {
int dissected_image_decrypt(DissectedImage *m, const char *passphrase, const void *root_hash, size_t root_hash_size, DissectImageFlags flags, DecryptedImage **ret);
int dissected_image_decrypt_interactively(DissectedImage *m, const char *passphrase, const void *root_hash, size_t root_hash_size, DissectImageFlags flags, DecryptedImage **ret);
-int dissected_image_mount(DissectedImage *m, const char *dest, DissectImageFlags flags);
+int dissected_image_mount(DissectedImage *m, const char *dest, uid_t uid_shift, DissectImageFlags flags);
int dissected_image_acquire_metadata(DissectedImage *m);
"STDIO or socket-activatable proxy to a given DBus endpoint.\n\n"
" -h --help Show this help\n"
" --version Show package version\n"
- " --bus-path=PATH Path to the kernel bus (default: %s)\n",
+ " -p --bus-path=PATH Path to the kernel bus (default: %s)\n",
program_invocation_short_name, DEFAULT_BUS_PATH);
return 0;
};
static const struct option options[] = {
- { "help", no_argument, NULL, 'h' },
- { "bus-path", required_argument, NULL, 'p' },
- { NULL, 0, NULL, 0 }
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, ARG_VERSION },
+ { "bus-path", required_argument, NULL, 'p' },
+ {},
};
int c;
[['src/test/test-async.c'],
[],
- []],
+ [],
+ '', 'timeout=120'],
[['src/test/test-locale-util.c'],
[],
assert_se(r > 0 && path_equal(result, "/etc"));
result = mfree(result);
+ r = chase_symlinks("/../.././//../../etc", NULL, 0, &result);
+ assert_se(r > 0);
+ assert_se(streq(result, "/etc"));
+ result = mfree(result);
+
+ r = chase_symlinks("/../.././//../../test-chase.fsldajfl", NULL, CHASE_NONEXISTENT, &result);
+ assert_se(r == 0);
+ assert_se(streq(result, "/test-chase.fsldajfl"));
+ result = mfree(result);
+
+ r = chase_symlinks("/../.././//../../etc", "/", CHASE_PREFIX_ROOT, &result);
+ assert_se(r > 0);
+ assert_se(streq(result, "/etc"));
+ result = mfree(result);
+
+ r = chase_symlinks("/../.././//../../test-chase.fsldajfl", "/", CHASE_PREFIX_ROOT|CHASE_NONEXISTENT, &result);
+ assert_se(r == 0);
+ assert_se(streq(result, "/test-chase.fsldajfl"));
+ result = mfree(result);
+
r = chase_symlinks("/etc/machine-id/foo", NULL, 0, &result);
assert_se(r == -ENOTDIR);
result = mfree(result);
char name2[] = "test-readlink_and_make_absolute/original";
char name_alias[] = "/tmp/test-readlink_and_make_absolute-alias";
char *r = NULL;
+ _cleanup_free_ char *pwd = NULL;
assert_se(mkdir_safe(tempdir, 0755, getuid(), getgid(), false) >= 0);
assert_se(touch(name) >= 0);
free(r);
assert_se(unlink(name_alias) >= 0);
+ assert_se(pwd = get_current_dir_name());
+
assert_se(chdir(tempdir) >= 0);
assert_se(symlink(name2, name_alias) >= 0);
assert_se(readlink_and_make_absolute(name_alias, &r) >= 0);
free(r);
assert_se(unlink(name_alias) >= 0);
+ assert_se(chdir(pwd) >= 0);
+
assert_se(rm_rf(tempdir, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
}
free(b64);
}
-static void test_unbase64mem(void) {
- void *mem;
- size_t len;
-
- assert_se(unbase64mem("", strlen(""), &mem, &len) == 0);
- assert_se(streq(strndupa(mem, len), ""));
- free(mem);
+static void test_unbase64mem_one(const char *input, const char *output, int ret) {
+ _cleanup_free_ void *buffer = NULL;
+ size_t size = 0;
- assert_se(unbase64mem("Zg==", strlen("Zg=="), &mem, &len) == 0);
- assert_se(streq(strndupa(mem, len), "f"));
- free(mem);
-
- assert_se(unbase64mem("Zm8=", strlen("Zm8="), &mem, &len) == 0);
- assert_se(streq(strndupa(mem, len), "fo"));
- free(mem);
+ assert_se(unbase64mem(input, (size_t) -1, &buffer, &size) == ret);
- assert_se(unbase64mem("Zm9v", strlen("Zm9v"), &mem, &len) == 0);
- assert_se(streq(strndupa(mem, len), "foo"));
- free(mem);
-
- assert_se(unbase64mem("Zm9vYg==", strlen("Zm9vYg=="), &mem, &len) == 0);
- assert_se(streq(strndupa(mem, len), "foob"));
- free(mem);
-
- assert_se(unbase64mem("Zm9vYmE=", strlen("Zm9vYmE="), &mem, &len) == 0);
- assert_se(streq(strndupa(mem, len), "fooba"));
- free(mem);
+ if (ret >= 0) {
+ assert_se(size == strlen(output));
+ assert_se(memcmp(buffer, output, size) == 0);
+ assert_se(((char*) buffer)[size] == 0);
+ }
+}
- assert_se(unbase64mem("Zm9vYmFy", strlen("Zm9vYmFy"), &mem, &len) == 0);
- assert_se(streq(strndupa(mem, len), "foobar"));
- free(mem);
+static void test_unbase64mem(void) {
- assert_se(unbase64mem("A", strlen("A"), &mem, &len) == -EINVAL);
- assert_se(unbase64mem("A====", strlen("A===="), &mem, &len) == -EINVAL);
- assert_se(unbase64mem("AAB==", strlen("AAB=="), &mem, &len) == -EINVAL);
- assert_se(unbase64mem("AAAB=", strlen("AAAB="), &mem, &len) == -EINVAL);
+ test_unbase64mem_one("", "", 0);
+ test_unbase64mem_one("Zg==", "f", 0);
+ test_unbase64mem_one("Zm8=", "fo", 0);
+ test_unbase64mem_one("Zm9v", "foo", 0);
+ test_unbase64mem_one("Zm9vYg==", "foob", 0);
+ test_unbase64mem_one("Zm9vYmE=", "fooba", 0);
+ test_unbase64mem_one("Zm9vYmFy", "foobar", 0);
+
+ test_unbase64mem_one(" ", "", 0);
+ test_unbase64mem_one(" \n\r ", "", 0);
+ test_unbase64mem_one(" Zg\n== ", "f", 0);
+ test_unbase64mem_one(" Zm 8=\r", "fo", 0);
+ test_unbase64mem_one(" Zm9\n\r\r\nv ", "foo", 0);
+ test_unbase64mem_one(" Z m9vYg==\n\r", "foob", 0);
+ test_unbase64mem_one(" Zm 9vYmE= ", "fooba", 0);
+ test_unbase64mem_one(" Z m9v YmFy ", "foobar", 0);
+
+ test_unbase64mem_one("A", NULL, -EPIPE);
+ test_unbase64mem_one("A====", NULL, -EINVAL);
+ test_unbase64mem_one("AAB==", NULL, -EINVAL);
+ test_unbase64mem_one(" A A A B = ", NULL, -EINVAL);
+ test_unbase64mem_one(" Z m 8 = q u u x ", NULL, -ENAMETOOLONG);
}
static void test_hexdump(void) {
}
static void test_strextend(void) {
- _cleanup_free_ char *str = strdup("0123");
- strextend(&str, "456", "78", "9", NULL);
- assert_se(streq(str, "0123456789"));
+ _cleanup_free_ char *str = NULL;
+
+ assert_se(strextend(&str, NULL));
+ assert_se(streq_ptr(str, ""));
+ assert_se(strextend(&str, "", "0", "", "", "123", NULL));
+ assert_se(streq_ptr(str, "0123"));
+ assert_se(strextend(&str, "456", "78", "9", NULL));
+ assert_se(streq_ptr(str, "0123456789"));
+}
+
+static void test_strextend_with_separator(void) {
+ _cleanup_free_ char *str = NULL;
+
+ assert_se(strextend_with_separator(&str, NULL, NULL));
+ assert_se(streq_ptr(str, ""));
+ str = mfree(str);
+
+ assert_se(strextend_with_separator(&str, "...", NULL));
+ assert_se(streq_ptr(str, ""));
+ assert_se(strextend_with_separator(&str, "...", NULL));
+ assert_se(streq_ptr(str, ""));
+ str = mfree(str);
+
+ assert_se(strextend_with_separator(&str, "xyz", "a", "bb", "ccc", NULL));
+ assert_se(streq_ptr(str, "axyzbbxyzccc"));
+ str = mfree(str);
+
+ assert_se(strextend_with_separator(&str, ",", "start", "", "1", "234", NULL));
+ assert_se(streq_ptr(str, "start,,1,234"));
+ assert_se(strextend_with_separator(&str, ";", "more", "5", "678", NULL));
+ assert_se(streq_ptr(str, "start,,1,234;more;5;678"));
}
static void test_strrep(void) {
test_streq_ptr();
test_strstrip();
test_strextend();
+ test_strextend_with_separator();
test_strrep();
test_strappend();
test_string_has_cc();
#include "time-util.h"
#include "udev-util.h"
#include "udev.h"
+#include "udevadm-util.h"
static void print_help(void) {
- printf("%s control COMMAND\n\n"
+ printf("%s control OPTION\n\n"
"Control the udev daemon.\n\n"
" -h --help Show this help\n"
- " --version Show package version\n"
+ " -V --version Show package version\n"
" -e --exit Instruct the daemon to cleanup and exit\n"
" -l --log-priority=LEVEL Set the udev log level for the daemon\n"
" -s --stop-exec-queue Do not execute events, queue only\n"
" -R --reload Reload rules and databases\n"
" -p --property=KEY=VALUE Set a global property for all events\n"
" -m --children-max=N Maximum number of children\n"
- " --timeout=SECONDS Maximum time to block for a reply\n"
+ " -t --timeout=SECONDS Maximum time to block for a reply\n"
, program_invocation_short_name);
}
{ "env", required_argument, NULL, 'p' }, /* alias for -p */
{ "children-max", required_argument, NULL, 'm' },
{ "timeout", required_argument, NULL, 't' },
+ { "version", no_argument, NULL, 'V' },
{ "help", no_argument, NULL, 'h' },
{}
};
if (uctrl == NULL)
return 2;
- while ((c = getopt_long(argc, argv, "el:sSRp:m:h", options, NULL)) >= 0)
+ while ((c = getopt_long(argc, argv, "el:sSRp:m:t:Vh", options, NULL)) >= 0)
switch (c) {
case 'e':
if (udev_ctrl_send_exit(uctrl, timeout) < 0)
}
break;
}
+ case 'V':
+ print_version();
+ rc = 0;
+ break;
case 'h':
print_help();
rc = 0;
#include "strbuf.h"
#include "string-util.h"
#include "udev.h"
+#include "udevadm-util.h"
#include "util.h"
/*
}
static void help(void) {
- printf("Usage: udevadm hwdb OPTIONS\n"
- " -u,--update update the hardware database\n"
- " --usr generate in " UDEVLIBEXECDIR " instead of /etc/udev\n"
- " -t,--test=MODALIAS query database and print result\n"
- " -r,--root=PATH alternative root path in the filesystem\n"
- " -h,--help\n\n");
+ printf("%s hwdb [OPTIONS]\n\n"
+ " -h --help Print this message\n"
+ " -V --version Print version of the program\n"
+ " -u --update Update the hardware database\n"
+ " --usr Generate in " UDEVLIBEXECDIR " instead of /etc/udev\n"
+ " -t --test=MODALIAS Query database and print result\n"
+ " -r --root=PATH Alternative root path in the filesystem\n\n"
+ "NOTE:\n"
+ "The sub-command 'hwdb' is deprecated, and is left for backwards compatibility.\n"
+ "Please use systemd-hwdb instead.\n"
+ , program_invocation_short_name);
}
static int adm_hwdb(struct udev *udev, int argc, char *argv[]) {
};
static const struct option options[] = {
- { "update", no_argument, NULL, 'u' },
- { "usr", no_argument, NULL, ARG_USR },
- { "test", required_argument, NULL, 't' },
- { "root", required_argument, NULL, 'r' },
- { "help", no_argument, NULL, 'h' },
+ { "update", no_argument, NULL, 'u' },
+ { "usr", no_argument, NULL, ARG_USR },
+ { "test", required_argument, NULL, 't' },
+ { "root", required_argument, NULL, 'r' },
+ { "version", no_argument, NULL, 'V' },
+ { "help", no_argument, NULL, 'h' },
{}
};
const char *test = NULL;
int err, c;
int rc = EXIT_SUCCESS;
- while ((c = getopt_long(argc, argv, "ut:r:h", options, NULL)) >= 0)
+ while ((c = getopt_long(argc, argv, "ut:r:Vh", options, NULL)) >= 0)
switch(c) {
case 'u':
update = true;
case 'r':
root = optarg;
break;
+ case 'V':
+ print_version();
+ return EXIT_SUCCESS;
case 'h':
help();
return EXIT_SUCCESS;
printf("%s info [OPTIONS] [DEVPATH|FILE]\n\n"
"Query sysfs or the udev database.\n\n"
" -h --help Print this message\n"
- " --version Print version of the program\n"
+ " -V --version Print version of the program\n"
" -q --query=TYPE Query device information:\n"
" name Name of device node\n"
" symlink Pointing to node\n"
export_prefix = optarg;
break;
case 'V':
- printf("%s\n", PACKAGE_VERSION);
+ print_version();
return 0;
case 'h':
help();
#include "format-util.h"
#include "udev-util.h"
#include "udev.h"
+#include "udevadm-util.h"
static bool udev_exit;
}
static void help(void) {
- printf("%s monitor [--property] [--kernel] [--udev] [--help]\n\n"
+ printf("%s monitor [OPTIONS]\n\n"
"Listen to kernel and udev events.\n\n"
" -h --help Show this help\n"
- " --version Show package version\n"
+ " -V --version Show package version\n"
" -p --property Print the event properties\n"
" -k --kernel Print kernel uevents\n"
" -u --udev Print udev events\n"
{ "udev", no_argument, NULL, 'u' },
{ "subsystem-match", required_argument, NULL, 's' },
{ "tag-match", required_argument, NULL, 't' },
+ { "version", no_argument, NULL, 'V' },
{ "help", no_argument, NULL, 'h' },
{}
};
udev_list_init(udev, &subsystem_match_list, true);
udev_list_init(udev, &tag_match_list, true);
- while ((c = getopt_long(argc, argv, "pekus:t:h", options, NULL)) >= 0)
+ while ((c = getopt_long(argc, argv, "pekus:t:Vh", options, NULL)) >= 0)
switch (c) {
case 'p':
case 'e':
case 't':
udev_list_entry_add(&tag_match_list, optarg, NULL);
break;
+ case 'V':
+ print_version();
+ return 0;
case 'h':
help();
return 0;
#include "parse-util.h"
#include "udev.h"
+#include "udevadm-util.h"
#include "util.h"
static void help(void) {
- printf("%s settle OPTIONS\n\n"
+ printf("%s settle [OPTIONS]\n\n"
"Wait for pending udev events.\n\n"
" -h --help Show this help\n"
- " --version Show package version\n"
+ " -V --version Show package version\n"
" -t --timeout=SECONDS Maximum time to wait for events\n"
" -E --exit-if-exists=FILE Stop waiting if file exists\n"
, program_invocation_short_name);
static const struct option options[] = {
{ "timeout", required_argument, NULL, 't' },
{ "exit-if-exists", required_argument, NULL, 'E' },
+ { "version", no_argument, NULL, 'V' },
{ "help", no_argument, NULL, 'h' },
{ "seq-start", required_argument, NULL, 's' }, /* removed */
{ "seq-end", required_argument, NULL, 'e' }, /* removed */
struct udev_queue *queue;
int rc = EXIT_FAILURE;
- while ((c = getopt_long(argc, argv, "t:E:hs:e:q", options, NULL)) >= 0) {
+ while ((c = getopt_long(argc, argv, "t:E:Vhs:e:q", options, NULL)) >= 0) {
switch (c) {
case 't': {
exists = optarg;
break;
+ case 'V':
+ print_version();
+ return EXIT_SUCCESS;
+
case 'h':
help();
return EXIT_SUCCESS;
#include "path-util.h"
#include "string-util.h"
#include "udev.h"
+#include "udevadm-util.h"
static void help(struct udev *udev) {
- printf("%s builtin [--help] COMMAND SYSPATH\n\n"
+ printf("%s test-builtin [OPTIONS] COMMAND DEVPATH\n\n"
"Test a built-in command.\n\n"
" -h --help Print this message\n"
- " --version Print version of the program\n\n"
+ " -V --version Print version of the program\n\n"
"Commands:\n"
, program_invocation_short_name);
static int adm_builtin(struct udev *udev, int argc, char *argv[]) {
static const struct option options[] = {
- { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, 'V' },
+ { "help", no_argument, NULL, 'h' },
{}
};
char *command = NULL;
enum udev_builtin_cmd cmd;
int rc = EXIT_SUCCESS, c;
- while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
+ while ((c = getopt_long(argc, argv, "Vh", options, NULL)) >= 0)
switch (c) {
+ case 'V':
+ print_version();
+ goto out;
case 'h':
help(udev);
goto out;
#include "string-util.h"
#include "udev-util.h"
#include "udev.h"
+#include "udevadm-util.h"
static void help(void) {
- printf("%s test OPTIONS <syspath>\n\n"
- "Test an event run.\n"
+ printf("%s test [OPTIONS] DEVPATH\n\n"
+ "Test an event run.\n\n"
" -h --help Show this help\n"
- " --version Show package version\n"
+ " -V --version Show package version\n"
" -a --action=ACTION Set action string\n"
" -N --resolve-names=early|late|never When to resolve names\n"
, program_invocation_short_name);
int rc = 0, c;
static const struct option options[] = {
- { "action", required_argument, NULL, 'a' },
+ { "action", required_argument, NULL, 'a' },
{ "resolve-names", required_argument, NULL, 'N' },
- { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, 'V' },
+ { "help", no_argument, NULL, 'h' },
{}
};
log_debug("version %s", PACKAGE_VERSION);
- while ((c = getopt_long(argc, argv, "a:N:h", options, NULL)) >= 0)
+ while ((c = getopt_long(argc, argv, "a:N:Vh", options, NULL)) >= 0)
switch (c) {
case 'a':
action = optarg;
exit(EXIT_FAILURE);
}
break;
+ case 'V':
+ print_version();
+ exit(EXIT_SUCCESS);
case 'h':
help();
exit(EXIT_SUCCESS);
}
static void help(void) {
- printf("%s trigger OPTIONS\n\n"
+ printf("%s trigger [OPTIONS] DEVPATH\n\n"
"Request events from the kernel.\n\n"
" -h --help Show this help\n"
- " --version Show package version\n"
+ " -V --version Show package version\n"
" -v --verbose Print the list of devices while running\n"
" -n --dry-run Do not actually trigger the events\n"
" -t --type= Type of events to trigger\n"
{ "sysname-match", required_argument, NULL, 'y' },
{ "name-match", required_argument, NULL, ARG_NAME },
{ "parent-match", required_argument, NULL, 'b' },
+ { "version", no_argument, NULL, 'V' },
{ "help", no_argument, NULL, 'h' },
{}
};
if (udev_enumerate == NULL)
return 1;
- while ((c = getopt_long(argc, argv, "vno:t:c:s:S:a:A:p:g:y:b:h", options, NULL)) >= 0) {
+ while ((c = getopt_long(argc, argv, "vnt:c:s:S:a:A:p:g:y:b:Vh", options, NULL)) >= 0) {
const char *key;
const char *val;
char buf[UTIL_PATH_SIZE];
break;
}
+ case 'V':
+ print_version();
+ return 0;
case 'h':
help();
return 0;
struct udev_device *find_device(struct udev *udev,
const char *id,
const char *prefix);
+
+static inline void print_version(void) {
+ printf("%s\n", PACKAGE_VERSION);
+}
printf("%s [OPTIONS...]\n\n"
"Manages devices.\n\n"
" -h --help Print this message\n"
- " --version Print version of the program\n"
- " --daemon Detach and run in the background\n"
- " --debug Enable debug output\n"
- " --children-max=INT Set maximum number of workers\n"
- " --exec-delay=SECONDS Seconds to wait before executing RUN=\n"
- " --event-timeout=SECONDS Seconds to wait before terminating an event\n"
- " --resolve-names=early|late|never\n"
+ " -V --version Print version of the program\n"
+ " -d --daemon Detach and run in the background\n"
+ " -D --debug Enable debug output\n"
+ " -c --children-max=INT Set maximum number of workers\n"
+ " -e --exec-delay=SECONDS Seconds to wait before executing RUN=\n"
+ " -t --event-timeout=SECONDS Seconds to wait before terminating an event\n"
+ " -N --resolve-names=early|late|never\n"
" When to resolve users and groups\n"
, program_invocation_short_name);
}
g video - - -
# Default group for normal users
-g users - - -
+g users @USERS_GID@ - -
# ex: ts=8 sw=4 sts=4 et filetype=sh
set -e
TEST_DESCRIPTION="https://github.com/systemd/systemd/issues/2467"
-TEST_NO_NSPAWN=1
. $TEST_BASE_DIR/test-functions
SKIP_INITRD=yes
eval $(udevadm info --export --query=env --name=${LOOPDEV}p2)
setup_basic_environment
- dracut_install nc true rm
+ dracut_install true rm
# setup the testsuite service
cat >$initdir/etc/systemd/system/testsuite.service <<'EOF'
[Service]
Type=oneshot
-ExecStart=/bin/sh -e -x -c 'rm -f /tmp/nonexistent; systemctl start test.socket; echo a | nc -U /run/test.ctl; >/testok'
+StandardOutput=tty
+StandardError=tty
+ExecStart=/bin/sh -e -x -c 'rm -f /tmp/nonexistent; systemctl start test.socket; echo > /run/test.ctl; >/testok'
TimeoutStartSec=10s
EOF
cat >$initdir/etc/systemd/system/test.socket <<'EOF'
[Socket]
-ListenStream=/run/test.ctl
+ListenFIFO=/run/test.ctl
EOF
cat > $initdir/etc/systemd/system/test.service <<'EOF'
setup_testsuite
) || return 1
+ setup_nspawn_root
# mask some services that we do not want to run in these tests
ln -s /dev/null $initdir/etc/systemd/system/systemd-hwdb-update.service
--- /dev/null
+#!/bin/bash -e
+
+if ! test -d ../build ; then
+ echo "Expected build directory in ../build, but couldn't find it." >&2
+ exit 1
+fi
+
+ninja -C ../build
+
+declare -A results
+
+RESULT=0
+FAILURES=0
+
+for TEST in TEST-??-* ; do
+ echo -e "\n--x-- Starting $TEST --x--"
+ set +e
+ make -C "$TEST" BUILD_DIR=$(pwd)/../build clean setup run
+ RESULT=$?
+ set -e
+ echo "--x-- Result of $TEST: $RESULT --x--"
+
+ results["$TEST"]="$RESULT"
+
+ [ "$RESULT" -ne "0" ] && FAILURES=$(($FAILURES+1))
+done
+
+echo ""
+
+for TEST in ${!results[@]}; do
+ RESULT="${results[$TEST]}"
+ if [ "$RESULT" -eq "0" ] ; then
+ echo "$TEST: SUCCESS"
+ else
+ echo "$TEST: FAIL"
+ fi
+done | sort
+
+if [ "$FAILURES" -eq 0 ] ; then
+ echo -e "\nALL PASSED"
+else
+ echo -e "\nTOTAL FAILURES: $FAILURES"
+fi
+
+exit "$FAILURES"