]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
Merge pull request #1466 from kaysievers/wip
authorTom Gundersen <teg@jklm.no>
Tue, 6 Oct 2015 09:57:53 +0000 (11:57 +0200)
committerTom Gundersen <teg@jklm.no>
Tue, 6 Oct 2015 09:57:53 +0000 (11:57 +0200)
libsystemd: sd-device - translate / vs. ! in sysname

48 files changed:
NEWS
configure.ac
man/journalctl.xml
man/journald.conf.xml
man/systemd-analyze.xml
man/systemd.link.xml
man/systemd.netdev.xml
man/systemd.network.xml
src/basic/missing.h
src/basic/util.c
src/core/service.c
src/journal-remote/journal-remote.c
src/journal/journal-file.c
src/journal/journal-file.h
src/journal/journal-vacuum.c
src/journal/journal-vacuum.h
src/journal/journalctl.c
src/journal/journald-gperf.gperf
src/journal/journald-server.c
src/journal/journald-server.h
src/journal/journald.c
src/journal/journald.conf
src/journal/test-journal-interleaving.c
src/journal/test-journal.c
src/libsystemd-network/lldp-internal.c
src/libsystemd-network/lldp-internal.h
src/libsystemd-network/lldp-network.c
src/libsystemd-network/lldp-network.h
src/libsystemd-network/lldp-port.c
src/libsystemd-network/lldp-tlv.c
src/libsystemd-network/lldp-tlv.h
src/libsystemd-network/lldp.h
src/libsystemd-network/sd-lldp.c
src/libsystemd-network/test-lldp.c
src/libsystemd/sd-netlink/netlink-message.c
src/libsystemd/sd-netlink/netlink-types.c
src/network/networkd-netdev-bridge.c
src/network/networkd-netdev-bridge.h
src/network/networkd-netdev-gperf.gperf
src/network/networkd-netdev.c
src/network/networkd-netdev.h
src/network/networkd-network-gperf.gperf
src/network/networkd-route.c
src/network/networkd-route.h
src/systemd/sd-lldp.h
src/systemd/sd-netlink.h
src/test/test-util.c
units/systemd-nspawn@.service.in

diff --git a/NEWS b/NEWS
index 80c347860907f4c531651cbae6896ea0395be20a..d987e116308487690963a1fac9d2ed2b6bd95e50 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -114,9 +114,13 @@ CHANGES WITH 227:
         * The RuntimeDirectory= setting now understands unit
           specifiers like %i or %f.
 
-        * networkd gained support for setting the IPv6 Router
-          Advertisment settings via IPv6AcceptRouterAdvertisements= in
-          .network files.
+        * networkd gained support for:
+            - setting the IPv6 Router Advertisment settings via
+              IPv6AcceptRouterAdvertisements= in .network files.
+            - configuring the HelloTimeSec, MaxAgeSec and
+              ForwardDelaySec bridge parameters in .netdev files.
+            - configuring PreferredSource for static routes in
+              .network files.
 
         * udev will now create /dev/disk/by-path links for ATA devices
           on kernels where that is supported.
index d75a02623bc63f43a7795ab5d1a524b7aa5e379b..aabb5e4fe48680889565355d43f87584316b6baf 100644 (file)
@@ -321,6 +321,7 @@ AC_CHECK_DECLS([IFLA_INET6_ADDR_GEN_MODE,
                 IFLA_GRE_ENCAP_DPORT,
                 IFLA_BRIDGE_VLAN_INFO,
                 IFLA_BRPORT_LEARNING_SYNC,
+                IFLA_BR_PRIORITY,
                 NDA_IFINDEX,
                 IFA_FLAGS],
 [], [], [[
index 305f1b9412dd1ef51627ad69df803f3f547459a9..db3f166e65349c35961c19598f9393ec819f4c95 100644 (file)
 
       <varlistentry>
         <term><option>-t</option></term>
-        <term><option>--identifier=<replaceable>SYSLOG_IDENTIFIER</replaceable>|<replaceable>PATTERN</replaceable></option></term>
+        <term><option>--identifier=<replaceable>SYSLOG_IDENTIFIER</replaceable></option></term>
 
         <listitem><para>Show messages for the specified syslog
-        identifier <replaceable>SYSLOG_IDENTIFIER</replaceable>, or
-        for any of the messages with a
-        <literal>SYSLOG_IDENTIFIER</literal> matched by
-        <replaceable>PATTERN</replaceable>.</para>
+        identifier
+        <replaceable>SYSLOG_IDENTIFIER</replaceable>.</para>
 
         <para>This parameter can be specified multiple
         times.</para></listitem>
       <varlistentry>
         <term><option>--vacuum-size=</option></term>
         <term><option>--vacuum-time=</option></term>
+        <term><option>--vacuum-files=</option></term>
 
         <listitem><para>Removes archived journal files until the disk
         space they use falls below the specified size (specified with
         timespan (specified with the usual <literal>s</literal>,
         <literal>min</literal>, <literal>h</literal>,
         <literal>days</literal>, <literal>months</literal>,
-        <literal>weeks</literal>, <literal>years</literal>
-        suffixes). Note that running <option>--vacuum-size=</option>
-        has only indirect effect on the output shown by
+        <literal>weeks</literal>, <literal>years</literal> suffixes),
+        or no more than the specified number of separate journal files
+        remain. Note that running <option>--vacuum-size=</option> has
+        only indirect effect on the output shown by
         <option>--disk-usage</option> as the latter includes active
-        journal files, while the former only operates on archived
-        journal files. <option>--vacuum-size=</option> and
-        <option>--vacuum-time=</option> may be combined in a single
-        invocation to enforce both a size and time limit on the
-        archived journal files.</para></listitem>
+        journal files, while the the vacuuming operation only operates
+        on archived journal files. Similar,
+        <option>--vacuum-files=</option> might not actually reduce the
+        number of journal files to below the specified number, as it
+        will not remove active journal
+        files. <option>--vacuum-size=</option>,
+        <option>--vacuum-time=</option> and
+        <option>--vacuum-files=</option> may be combined in a single
+        invocation to enforce any combination of a size, a time and a
+        number of files limit on the archived journal
+        files. Specifying any of these three parameters as zero is
+        equivalent to not enforcing the specific limit, and is thus
+        redundant.</para></listitem>
       </varlistentry>
 
       <varlistentry>
index d6fe45d40c4720f11b45a3893412a71151c1799b..4464fe53ad769873be9ce5f214e576f9cf9e5b05 100644 (file)
         <term><varname>SystemMaxUse=</varname></term>
         <term><varname>SystemKeepFree=</varname></term>
         <term><varname>SystemMaxFileSize=</varname></term>
+        <term><varname>SystemMaxFiles=</varname></term>
         <term><varname>RuntimeMaxUse=</varname></term>
         <term><varname>RuntimeKeepFree=</varname></term>
         <term><varname>RuntimeMaxFileSize=</varname></term>
+        <term><varname>RuntimeMaxFiles=</varname></term>
 
         <listitem><para>Enforce size limits on the journal files
         stored. The options prefixed with <literal>System</literal>
         names not ending with <literal>.journal</literal> or
         <literal>.journal~</literal>, so only such files, located in
         the appropriate directories, are taken into account when
-        calculating current disk usage.
-        </para>
+        calculating current disk usage.</para>
 
         <para><varname>SystemMaxUse=</varname> and
         <varname>RuntimeMaxUse=</varname> control how much disk space
         and use the smaller of the two values.</para>
 
         <para>The first pair defaults to 10% and the second to 15% of
-        the size of the respective file system. If the file system is
-        nearly full and either <varname>SystemKeepFree=</varname> or
-        <varname>RuntimeKeepFree=</varname> is violated when
-        systemd-journald is started, the value will be raised to
+        the size of the respective file system, but each value is
+        capped to 4G. If the file system is nearly full and either
+        <varname>SystemKeepFree=</varname> or
+        <varname>RuntimeKeepFree=</varname> are violated when
+        systemd-journald is started, the limit will be raised to the
         percentage that is actually free. This means that if there was
         enough free space before and journal files were created, and
         subsequently something else causes the file system to fill up,
         journald will stop using more space, but it will not be
-        removing existing files to go reduce footprint either.</para>
+        removing existing files to reduce footprint again
+        either.</para>
 
         <para><varname>SystemMaxFileSize=</varname> and
         <varname>RuntimeMaxFileSize=</varname> control how large
         eighth of the values configured with
         <varname>SystemMaxUse=</varname> and
         <varname>RuntimeMaxUse=</varname>, so that usually seven
-        rotated journal files are kept as history.</para></listitem>
+        rotated journal files are kept as history.</para>
 
         <para>Specify values in bytes or use K, M, G, T, P, E as
         units for the specified sizes (equal to 1024, 1024²,... bytes).
         Note that size limits are enforced synchronously when journal
         files are extended, and no explicit rotation step triggered by
         time is needed.</para>
+
+        <para><varname>SystemMaxFiles=</varname> and
+        <varname>RuntimeMaxFiles=</varname> control how many
+        individual journal files to keep at maximum. Note that only
+        archived files are deleted to reduce the number of files until
+        this limit is reached; active files will stay around. This
+        means that in effect there might still be more journal files
+        around in total than this limit after a vacuuming operation is
+        complete. This setting defaults to 100.</para></listitem>
       </varlistentry>
 
       <varlistentry>
index 58e6fd17803e883d39a5abb916b1c1ca3ae262aa..632e6363f6705c8b916c96563810089e101be4ff 100644 (file)
@@ -93,7 +93,7 @@
       <command>systemd-analyze</command>
       <arg choice="opt" rep="repeat">OPTIONS</arg>
       <arg choice="plain">set-log-level</arg>
-      <arg choice="opt"><replaceable>LEVEL</replaceable></arg>
+      <arg choice="plain"><replaceable>LEVEL</replaceable></arg>
     </cmdsynopsis>
     <cmdsynopsis>
       <command>systemd-analyze</command>
index b630ef7a17ed7b2eb6c9133e2931a25f7ec3d9cf..dfff785e468ee67cf383ac13c1e0a1f4505a9af1 100644 (file)
   <refsect1>
     <title>Example</title>
     <example>
-      <title>/etc/systemd/network/wireless.link</title>
+      <title>/etc/systemd/network/25-wireless.link</title>
 
       <programlisting>[Match]
 MACAddress=12:34:56:78:9a:bc
index 05bbad7f65500911f53df851b5ac569d6a121e41..70311ca9d9e7a2aec7a6254acb273131b6e2bc55 100644 (file)
       </variablelist>
   </refsect1>
 
+    <refsect1>
+    <title>[Bridge] Section Options</title>
+
+      <para>The <literal>[Bridge]</literal> section only applies for
+      netdevs of kind <literal>bridge</literal>, and accepts the
+      following key:</para>
+
+      <variablelist class='network-directives'>
+        <varlistentry>
+          <term><varname>HelloTimeSec=</varname></term>
+          <listitem>
+            <para>HelloTimeSec specifies the number of seconds a hello packet is
+            sent out by the root bridge and the designated bridges. Hello packets are
+            used to communicate information about the topology throughout the entire
+            bridged local area network.</para>
+          </listitem>
+        </varlistentry>
+        <varlistentry>
+          <term><varname>MaxAgeSec=</varname></term>
+          <listitem>
+            <para>MaxAgeSec specifies the number of seconds of maximum message age.
+            If the last seen (received) hello packet is more than this number of
+            seconds old, the bridge in question will start the takeover procedure
+            in attempt to become the Root Bridge itself.</para>
+          </listitem>
+        </varlistentry>
+        <varlistentry>
+          <term><varname>ForwardDelaySec=</varname></term>
+          <listitem>
+            <para>ForwardDelaySec specifies the number of seconds spent in each
+            of the Listening and Learning states before the Forwarding state is entered.</para>
+          </listitem>
+        </varlistentry>
+      </variablelist>
+
+  </refsect1>
+
   <refsect1>
     <title>[VLAN] Section Options</title>
 
   <refsect1>
     <title>Example</title>
     <example>
-      <title>/etc/systemd/network/bridge.netdev</title>
+      <title>/etc/systemd/network/25-bridge.netdev</title>
 
       <programlisting>[NetDev]
 Name=bridge0
@@ -917,7 +954,7 @@ Kind=bridge</programlisting>
     </example>
 
     <example>
-      <title>/etc/systemd/network/vlan1.netdev</title>
+      <title>/etc/systemd/network/25-vlan1.netdev</title>
 
       <programlisting>[Match]
 Virtualization=no
@@ -930,7 +967,7 @@ Kind=vlan
 Id=1</programlisting>
     </example>
     <example>
-      <title>/etc/systemd/network/ipip.netdev</title>
+      <title>/etc/systemd/network/25-ipip.netdev</title>
       <programlisting>[NetDev]
 Name=ipip-tun
 Kind=ipip
@@ -942,7 +979,7 @@ Remote=192.169.224.239
 TTL=64</programlisting>
     </example>
     <example>
-      <title>/etc/systemd/network/tap.netdev</title>
+      <title>/etc/systemd/network/25-tap.netdev</title>
       <programlisting>[NetDev]
 Name=tap-test
 Kind=tap
@@ -952,7 +989,7 @@ MultiQueue=true
 PacketInfo=true</programlisting> </example>
 
     <example>
-      <title>/etc/systemd/network/sit.netdev</title>
+      <title>/etc/systemd/network/25-sit.netdev</title>
       <programlisting>[NetDev]
 Name=sit-tun
 Kind=sit
@@ -964,7 +1001,7 @@ Remote=10.65.223.239</programlisting>
     </example>
 
     <example>
-      <title>/etc/systemd/network/gre.netdev</title>
+      <title>/etc/systemd/network/25-gre.netdev</title>
       <programlisting>[NetDev]
 Name=gre-tun
 Kind=gre
@@ -976,7 +1013,7 @@ Remote=10.65.223.239</programlisting>
     </example>
 
     <example>
-      <title>/etc/systemd/network/vti.netdev</title>
+      <title>/etc/systemd/network/25-vti.netdev</title>
 
       <programlisting>[NetDev]
 Name=vti-tun
@@ -989,7 +1026,7 @@ Remote=10.65.223.239</programlisting>
     </example>
 
     <example>
-      <title>/etc/systemd/network/veth.netdev</title>
+      <title>/etc/systemd/network/25-veth.netdev</title>
       <programlisting>[NetDev]
 Name=veth-test
 Kind=veth
@@ -999,7 +1036,7 @@ Name=veth-peer</programlisting>
     </example>
 
     <example>
-      <title>/etc/systemd/network/bond.netdev</title>
+      <title>/etc/systemd/network/25-bond.netdev</title>
       <programlisting>[NetDev]
 Name=bond1
 Kind=bond
@@ -1013,7 +1050,7 @@ LACPTransmitRate=fast
     </example>
 
     <example>
-      <title>/etc/systemd/network/dummy.netdev</title>
+      <title>/etc/systemd/network/25-dummy.netdev</title>
       <programlisting>[NetDev]
 Name=dummy-test
 Kind=dummy
index 629088ea8166bb5fedcc794cbe7fe1518653792f..a27f2ff99e1b4d7ff74cebd89c88551e0103df0c 100644 (file)
             <literal>global</literal>.</para>
           </listitem>
         </varlistentry>
+        <varlistentry>
+          <term><varname>PreferredSource=</varname></term>
+          <listitem>
+            <para>The preferred source address of the route. The address
+            must be in the format described in
+            <citerefentry project='man-pages'><refentrytitle>inet_pton</refentrytitle><manvolnum>3</manvolnum></citerefentry>.</para>
+          </listitem>
+        </varlistentry>
       </variablelist>
   </refsect1>
 
@@ -893,7 +901,7 @@ DHCP=yes</programlisting>
     </example>
 
     <example>
-      <title>/etc/systemd/network/bridge-static.network</title>
+      <title>/etc/systemd/network/25-bridge-static.network</title>
 
       <programlisting>[Match]
 Name=bridge0
@@ -905,7 +913,7 @@ DNS=192.168.0.1</programlisting>
     </example>
 
     <example>
-      <title>/etc/systemd/network/bridge-slave-interface.network</title>
+      <title>/etc/systemd/network/25-bridge-slave-interface.network</title>
 
       <programlisting>[Match]
 Name=enp2s0
@@ -914,7 +922,7 @@ Name=enp2s0
 Bridge=bridge0</programlisting>
     </example>
     <example>
-      <title>/etc/systemd/network/ipip.network</title>
+      <title>/etc/systemd/network/25-ipip.network</title>
 
       <programlisting>[Match]
 Name=em1
@@ -924,7 +932,7 @@ Tunnel=ipip-tun</programlisting>
     </example>
 
     <example>
-      <title>/etc/systemd/network/sit.network</title>
+      <title>/etc/systemd/network/25-sit.network</title>
 
       <programlisting>[Match]
 Name=em1
@@ -934,7 +942,7 @@ Tunnel=sit-tun</programlisting>
     </example>
 
     <example>
-      <title>/etc/systemd/network/gre.network</title>
+      <title>/etc/systemd/network/25-gre.network</title>
 
       <programlisting>[Match]
 Name=em1
@@ -944,7 +952,7 @@ Tunnel=gre-tun</programlisting>
     </example>
 
     <example>
-      <title>/etc/systemd/network/vti.network</title>
+      <title>/etc/systemd/network/25-vti.network</title>
 
       <programlisting>[Match]
 Name=em1
@@ -954,7 +962,7 @@ Tunnel=vti-tun</programlisting>
     </example>
 
     <example>
-      <title>/etc/systemd/network/bond.network</title>
+      <title>/etc/systemd/network/25-bond.network</title>
 
       <programlisting>[Match]
 Name=bond1
index 9811b6b23e4e7b6fe930a4f6722412614158252a..1e3af283bb3496e5b819cf5f12f235ed550aa6ab 100644 (file)
@@ -842,6 +842,19 @@ static inline int setns(int fd, int nstype) {
 #define IFLA_BRIDGE_MAX (__IFLA_BRIDGE_MAX - 1)
 #endif
 
+#if !HAVE_DECL_IFLA_BR_PRIORITY
+#define IFLA_BR_UNSPEC 0
+#define IFLA_BR_FORWARD_DELAY 1
+#define IFLA_BR_HELLO_TIME 2
+#define IFLA_BR_MAX_AGE 3
+#define IFLA_BR_AGEING_TIME 4
+#define IFLA_BR_STP_STATE 5
+#define IFLA_BR_PRIORITY 6
+#define __IFLA_BR_MAX 7
+
+#define IFLA_BR_MAX (__IFLA_BR_MAX - 1)
+#endif
+
 #if !HAVE_DECL_IFLA_BRPORT_LEARNING_SYNC
 #define IFLA_BRPORT_UNSPEC 0
 #define IFLA_BRPORT_STATE 1
index c63ec0ceb06179352c1f15007a331503faf81c42..9f520462cfd6180d3f1105e4f301e140c125f1a2 100644 (file)
@@ -6168,16 +6168,19 @@ int openpt_in_namespace(pid_t pid, int flags) {
 }
 
 ssize_t fgetxattrat_fake(int dirfd, const char *filename, const char *attribute, void *value, size_t size, int flags) {
+        char fn[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1];
         _cleanup_close_ int fd = -1;
         ssize_t l;
 
         /* The kernel doesn't have a fgetxattrat() command, hence let's emulate one */
 
-        fd = openat(dirfd, filename, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOATIME|(flags & AT_SYMLINK_NOFOLLOW ? O_NOFOLLOW : 0));
+        fd = openat(dirfd, filename, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_PATH|(flags & AT_SYMLINK_NOFOLLOW ? O_NOFOLLOW : 0));
         if (fd < 0)
                 return -errno;
 
-        l = fgetxattr(fd, attribute, value, size);
+        xsprintf(fn, "/proc/self/fd/%i", fd);
+
+        l = getxattr(fn, attribute, value, size);
         if (l < 0)
                 return -errno;
 
index cb0394f9303b46d42e9964ca58318c0fcef06add..3bb0d913b4606c4ce45fd45efb1e249a2fb3fb93 100644 (file)
@@ -884,7 +884,6 @@ static void service_set_state(Service *s, ServiceState state) {
                 log_unit_debug(UNIT(s), "Changed %s -> %s", service_state_to_string(old_state), service_state_to_string(state));
 
         unit_notify(UNIT(s), table[old_state], table[state], s->reload_result == SERVICE_SUCCESS);
-        s->reload_result = SERVICE_SUCCESS;
 }
 
 static int service_coldplug(Unit *u) {
@@ -1778,6 +1777,7 @@ static void service_enter_reload(Service *s) {
         assert(s);
 
         service_unwatch_control_pid(s);
+        s->reload_result = SERVICE_SUCCESS;
 
         s->control_command = s->exec_command[SERVICE_EXEC_RELOAD];
         if (s->control_command) {
index 5354bf6e51d97e56274e9cf52834bb1a7228acfa..c920ef76268759e2a31178016e8c181c3fc4dceb 100644 (file)
@@ -954,7 +954,7 @@ static int remoteserver_init(RemoteServer *s,
         }
 
         if (s->active == 0) {
-                log_error("Zarro sources specified");
+                log_error("Zero sources specified");
                 return -EINVAL;
         }
 
index 73d3a4bb9d2307f27a6484551fcb420b24c58f4b..1071c6d6d73dfa34df64f026266b82ba08b53f1b 100644 (file)
@@ -49,6 +49,9 @@
 #define DEFAULT_MAX_USE_LOWER (1ULL*1024ULL*1024ULL)           /* 1 MiB */
 #define DEFAULT_MAX_USE_UPPER (4ULL*1024ULL*1024ULL*1024ULL)   /* 4 GiB */
 
+/* This is the default minimal use limit, how much we'll use even if keep_free suggests otherwise. */
+#define DEFAULT_MIN_USE (1ULL*1024ULL*1024ULL)                 /* 1 MiB */
+
 /* This is the upper bound if we deduce max_size from max_use */
 #define DEFAULT_MAX_SIZE_UPPER (128ULL*1024ULL*1024ULL)        /* 128 MiB */
 
@@ -60,6 +63,9 @@
  * size */
 #define DEFAULT_KEEP_FREE (1024ULL*1024ULL)                    /* 1 MB */
 
+/* This is the default maximum number of journal files to keep around. */
+#define DEFAULT_N_MAX_FILES (100)
+
 /* n_data was the first entry we added after the initial file format design */
 #define HEADER_SIZE_MIN ALIGN64(offsetof(Header, n_data))
 
@@ -128,7 +134,7 @@ int journal_file_set_offline(JournalFile *f) {
         return 0;
 }
 
-void journal_file_close(JournalFile *f) {
+JournalFile* journal_file_close(JournalFile *f) {
         assert(f);
 
 #ifdef HAVE_GCRYPT
@@ -179,6 +185,7 @@ void journal_file_close(JournalFile *f) {
 #endif
 
         free(f);
+        return NULL;
 }
 
 static int journal_file_init_header(JournalFile *f, JournalFile *template) {
@@ -398,12 +405,7 @@ static int journal_file_allocate(JournalFile *f, uint64_t offset, uint64_t size)
                 if (fstatvfs(f->fd, &svfs) >= 0) {
                         uint64_t available;
 
-                        available = svfs.f_bfree * svfs.f_bsize;
-
-                        if (available >= f->metrics.keep_free)
-                                available -= f->metrics.keep_free;
-                        else
-                                available = 0;
+                        available = LESS_BY((uint64_t) svfs.f_bfree * (uint64_t) svfs.f_bsize, f->metrics.keep_free);
 
                         if (new_size - old_size > available)
                                 return -E2BIG;
@@ -604,10 +606,10 @@ static int journal_file_setup_data_hash_table(JournalFile *f) {
 
         assert(f);
 
-        /* We estimate that we need 1 hash table entry per 768 of
-           journal file and we want to make sure we never get beyond
-           75% fill level. Calculate the hash table size for the
-           maximum file size based on these metrics. */
+        /* We estimate that we need 1 hash table entry per 768 bytes
+           of journal file and we want to make sure we never get
+           beyond 75% fill level. Calculate the hash table size for
+           the maximum file size based on these metrics. */
 
         s = (f->metrics.max_size * 4 / 768 / 3) * sizeof(HashItem);
         if (s < DEFAULT_DATA_HASH_TABLE_SIZE)
@@ -2833,8 +2835,7 @@ int journal_file_open_reliably(
         size_t l;
         _cleanup_free_ char *p = NULL;
 
-        r = journal_file_open(fname, flags, mode, compress, seal,
-                              metrics, mmap_cache, template, ret);
+        r = journal_file_open(fname, flags, mode, compress, seal, metrics, mmap_cache, template, ret);
         if (!IN_SET(r,
                     -EBADMSG,           /* corrupted */
                     -ENODATA,           /* truncated */
@@ -2864,8 +2865,7 @@ int journal_file_open_reliably(
                      random_u64()) < 0)
                 return -ENOMEM;
 
-        r = rename(fname, p);
-        if (r < 0)
+        if (rename(fname, p) < 0)
                 return -errno;
 
         /* btrfs doesn't cope well with our write pattern and
@@ -2874,10 +2874,9 @@ int journal_file_open_reliably(
         (void) chattr_path(p, false, FS_NOCOW_FL);
         (void) btrfs_defrag(p);
 
-        log_warning("File %s corrupted or uncleanly shut down, renaming and replacing.", fname);
+        log_warning_errno(r, "File %s corrupted or uncleanly shut down, renaming and replacing.", fname);
 
-        return journal_file_open(fname, flags, mode, compress, seal,
-                                 metrics, mmap_cache, template, ret);
+        return journal_file_open(fname, flags, mode, compress, seal, metrics, mmap_cache, template, ret);
 }
 
 int journal_file_copy_entry(JournalFile *from, JournalFile *to, Object *o, uint64_t p, uint64_t *seqnum, Object **ret, uint64_t *offset) {
@@ -2964,16 +2963,35 @@ int journal_file_copy_entry(JournalFile *from, JournalFile *to, Object *o, uint6
         return r;
 }
 
+void journal_reset_metrics(JournalMetrics *m) {
+        assert(m);
+
+        /* Set everything to "pick automatic values". */
+
+        *m = (JournalMetrics) {
+                .min_use = (uint64_t) -1,
+                .max_use = (uint64_t) -1,
+                .min_size = (uint64_t) -1,
+                .max_size = (uint64_t) -1,
+                .keep_free = (uint64_t) -1,
+                .n_max_files = (uint64_t) -1,
+        };
+}
+
 void journal_default_metrics(JournalMetrics *m, int fd) {
-        uint64_t fs_size = 0;
+        char a[FORMAT_BYTES_MAX], b[FORMAT_BYTES_MAX], c[FORMAT_BYTES_MAX], d[FORMAT_BYTES_MAX], e[FORMAT_BYTES_MAX];
         struct statvfs ss;
-        char a[FORMAT_BYTES_MAX], b[FORMAT_BYTES_MAX], c[FORMAT_BYTES_MAX], d[FORMAT_BYTES_MAX];
+        uint64_t fs_size;
 
         assert(m);
         assert(fd >= 0);
 
         if (fstatvfs(fd, &ss) >= 0)
                 fs_size = ss.f_frsize * ss.f_blocks;
+        else {
+                log_debug_errno(errno, "Failed to detremine disk size: %m");
+                fs_size = 0;
+        }
 
         if (m->max_use == (uint64_t) -1) {
 
@@ -2990,10 +3008,16 @@ void journal_default_metrics(JournalMetrics *m, int fd) {
         } else {
                 m->max_use = PAGE_ALIGN(m->max_use);
 
-                if (m->max_use < JOURNAL_FILE_SIZE_MIN*2)
+                if (m->max_use != 0 && m->max_use < JOURNAL_FILE_SIZE_MIN*2)
                         m->max_use = JOURNAL_FILE_SIZE_MIN*2;
         }
 
+        if (m->min_use == (uint64_t) -1)
+                m->min_use = DEFAULT_MIN_USE;
+
+        if (m->min_use > m->max_use)
+                m->min_use = m->max_use;
+
         if (m->max_size == (uint64_t) -1) {
                 m->max_size = PAGE_ALIGN(m->max_use / 8); /* 8 chunks */
 
@@ -3002,11 +3026,13 @@ void journal_default_metrics(JournalMetrics *m, int fd) {
         } else
                 m->max_size = PAGE_ALIGN(m->max_size);
 
-        if (m->max_size < JOURNAL_FILE_SIZE_MIN)
-                m->max_size = JOURNAL_FILE_SIZE_MIN;
+        if (m->max_size != 0) {
+                if (m->max_size < JOURNAL_FILE_SIZE_MIN)
+                        m->max_size = JOURNAL_FILE_SIZE_MIN;
 
-        if (m->max_size*2 > m->max_use)
-                m->max_use = m->max_size*2;
+                if (m->max_use != 0 && m->max_size*2 > m->max_use)
+                        m->max_use = m->max_size*2;
+        }
 
         if (m->min_size == (uint64_t) -1)
                 m->min_size = JOURNAL_FILE_SIZE_MIN;
@@ -3016,7 +3042,7 @@ void journal_default_metrics(JournalMetrics *m, int fd) {
                 if (m->min_size < JOURNAL_FILE_SIZE_MIN)
                         m->min_size = JOURNAL_FILE_SIZE_MIN;
 
-                if (m->min_size > m->max_size)
+                if (m->max_size != 0 && m->min_size > m->max_size)
                         m->max_size = m->min_size;
         }
 
@@ -3032,11 +3058,16 @@ void journal_default_metrics(JournalMetrics *m, int fd) {
                         m->keep_free = DEFAULT_KEEP_FREE;
         }
 
-        log_debug("Fixed max_use=%s max_size=%s min_size=%s keep_free=%s",
-                  format_bytes(a, sizeof(a), m->max_use),
-                  format_bytes(b, sizeof(b), m->max_size),
-                  format_bytes(c, sizeof(c), m->min_size),
-                  format_bytes(d, sizeof(d), m->keep_free));
+        if (m->n_max_files == (uint64_t) -1)
+                m->n_max_files = DEFAULT_N_MAX_FILES;
+
+        log_debug("Fixed min_use=%s max_use=%s max_size=%s min_size=%s keep_free=%s n_max_files=%" PRIu64,
+                  format_bytes(a, sizeof(a), m->min_use),
+                  format_bytes(b, sizeof(b), m->max_use),
+                  format_bytes(c, sizeof(c), m->max_size),
+                  format_bytes(d, sizeof(d), m->min_size),
+                  format_bytes(e, sizeof(e), m->keep_free),
+                  m->n_max_files);
 }
 
 int journal_file_get_cutoff_realtime_usec(JournalFile *f, usec_t *from, usec_t *to) {
index e92b75eabe8ff56bc7b30ceadb9f08b19e2ee50e..f2c07356c82c543e2d21fdb8c5d24edb5e461c36 100644 (file)
 #include "hashmap.h"
 
 typedef struct JournalMetrics {
-        uint64_t max_use;
-        uint64_t use;
-        uint64_t max_size;
-        uint64_t min_size;
-        uint64_t keep_free;
+        /* For all these: -1 means "pick automatically", and 0 means "no limit enforced" */
+        uint64_t max_size;     /* how large journal files grow at max */
+        uint64_t min_size;     /* how large journal files grow at least */
+        uint64_t max_use;      /* how much disk space to use in total at max, keep_free permitting */
+        uint64_t min_use;      /* how much disk space to use in total at least, even if keep_free says not to */
+        uint64_t keep_free;    /* how much to keep free on disk */
+        uint64_t n_max_files;  /* how many files to keep around at max */
 } JournalMetrics;
 
 typedef enum direction {
@@ -136,7 +138,7 @@ int journal_file_open(
                 JournalFile **ret);
 
 int journal_file_set_offline(JournalFile *f);
-void journal_file_close(JournalFile *j);
+JournalFile* journal_file_close(JournalFile *j);
 
 int journal_file_open_reliably(
                 const char *fname,
@@ -223,6 +225,7 @@ int journal_file_rotate(JournalFile **f, bool compress, bool seal);
 
 void journal_file_post_change(JournalFile *f);
 
+void journal_reset_metrics(JournalMetrics *m);
 void journal_default_metrics(JournalMetrics *m, int fd);
 
 int journal_file_get_cutoff_realtime_usec(JournalFile *f, usec_t *from, usec_t *to);
index 17499bbc303a76d8027f7bd3798d764316fceb38..a394066cb4e2ef9322ee95520fae3568321a55b4 100644 (file)
@@ -34,9 +34,9 @@ struct vacuum_info {
         char *filename;
 
         uint64_t realtime;
+
         sd_id128_t seqnum_id;
         uint64_t seqnum;
-
         bool have_seqnum;
 };
 
@@ -67,19 +67,18 @@ static int vacuum_compare(const void *_a, const void *_b) {
 }
 
 static void patch_realtime(
-                const char *dir,
+                int fd,
                 const char *fn,
                 const struct stat *st,
                 unsigned long long *realtime) {
 
-        _cleanup_free_ char *path = NULL;
         usec_t x, crtime = 0;
 
         /* The timestamp was determined by the file name, but let's
          * see if the file might actually be older than the file name
          * suggested... */
 
-        assert(dir);
+        assert(fd >= 0);
         assert(fn);
         assert(st);
         assert(realtime);
@@ -101,14 +100,7 @@ static void patch_realtime(
          * unfortunately there's currently no sane API to query
          * it. Hence let's implement this manually... */
 
-        /* Unfortunately there is is not fgetxattrat(), so we need to
-         * go via path here. :-( */
-
-        path = strjoin(dir, "/", fn, NULL);
-        if (!path)
-                return;
-
-        if (path_getcrtime(path, &crtime) >= 0) {
+        if (fd_getcrtime_at(fd, fn, &crtime, 0) >= 0) {
                 if (crtime < *realtime)
                         *realtime = crtime;
         }
@@ -120,9 +112,13 @@ static int journal_file_empty(int dir_fd, const char *name) {
         le64_t n_entries;
         ssize_t n;
 
-        fd = openat(dir_fd, name, O_RDONLY|O_CLOEXEC|O_NOFOLLOW|O_NONBLOCK);
-        if (fd < 0)
-                return -errno;
+        fd = openat(dir_fd, name, O_RDONLY|O_CLOEXEC|O_NOFOLLOW|O_NONBLOCK|O_NOATIME);
+        if (fd < 0) {
+                /* Maybe failed due to O_NOATIME and lack of privileges? */
+                fd = openat(dir_fd, name, O_RDONLY|O_CLOEXEC|O_NOFOLLOW|O_NONBLOCK);
+                if (fd < 0)
+                        return -errno;
+        }
 
         if (fstat(fd, &st) < 0)
                 return -errno;
@@ -144,22 +140,24 @@ static int journal_file_empty(int dir_fd, const char *name) {
 int journal_directory_vacuum(
                 const char *directory,
                 uint64_t max_use,
+                uint64_t n_max_files,
                 usec_t max_retention_usec,
                 usec_t *oldest_usec,
                 bool verbose) {
 
         _cleanup_closedir_ DIR *d = NULL;
-        int r = 0;
         struct vacuum_info *list = NULL;
-        unsigned n_list = 0, i;
+        unsigned n_list = 0, i, n_active_files = 0;
         size_t n_allocated = 0;
         uint64_t sum = 0, freed = 0;
         usec_t retention_limit = 0;
         char sbytes[FORMAT_BYTES_MAX];
+        struct dirent *de;
+        int r;
 
         assert(directory);
 
-        if (max_use <= 0 && max_retention_usec <= 0)
+        if (max_use <= 0 && max_retention_usec <= 0 && n_max_files <= 0)
                 return 0;
 
         if (max_retention_usec > 0) {
@@ -174,27 +172,20 @@ int journal_directory_vacuum(
         if (!d)
                 return -errno;
 
-        for (;;) {
-                struct dirent *de;
-                size_t q;
-                struct stat st;
-                char *p;
+        FOREACH_DIRENT_ALL(de, d, r = -errno; goto finish) {
+
                 unsigned long long seqnum = 0, realtime;
+                _cleanup_free_ char *p = NULL;
                 sd_id128_t seqnum_id;
                 bool have_seqnum;
+                uint64_t size;
+                struct stat st;
+                size_t q;
 
-                errno = 0;
-                de = readdir(d);
-                if (!de && errno != 0) {
-                        r = -errno;
-                        goto finish;
-                }
-
-                if (!de)
-                        break;
-
-                if (fstatat(dirfd(d), de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0)
+                if (fstatat(dirfd(d), de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
+                        log_debug_errno(errno, "Failed to stat file %s while vacuuming, ignoring: %m", de->d_name);
                         continue;
+                }
 
                 if (!S_ISREG(st.st_mode))
                         continue;
@@ -203,15 +194,20 @@ int journal_directory_vacuum(
 
                 if (endswith(de->d_name, ".journal")) {
 
-                        /* Vacuum archived files */
+                        /* Vacuum archived files. Active files are
+                         * left around */
 
-                        if (q < 1 + 32 + 1 + 16 + 1 + 16 + 8)
+                        if (q < 1 + 32 + 1 + 16 + 1 + 16 + 8) {
+                                n_active_files++;
                                 continue;
+                        }
 
                         if (de->d_name[q-8-16-1] != '-' ||
                             de->d_name[q-8-16-1-16-1] != '-' ||
-                            de->d_name[q-8-16-1-16-1-32-1] != '@')
+                            de->d_name[q-8-16-1-16-1-32-1] != '@') {
+                                n_active_files++;
                                 continue;
+                        }
 
                         p = strdup(de->d_name);
                         if (!p) {
@@ -222,11 +218,13 @@ int journal_directory_vacuum(
                         de->d_name[q-8-16-1-16-1] = 0;
                         if (sd_id128_from_string(de->d_name + q-8-16-1-16-1-32, &seqnum_id) < 0) {
                                 free(p);
+                                n_active_files++;
                                 continue;
                         }
 
                         if (sscanf(de->d_name + q-8-16-1-16, "%16llx-%16llx.journal", &seqnum, &realtime) != 2) {
                                 free(p);
+                                n_active_files++;
                                 continue;
                         }
 
@@ -237,12 +235,16 @@ int journal_directory_vacuum(
 
                         /* Vacuum corrupted files */
 
-                        if (q < 1 + 16 + 1 + 16 + 8 + 1)
+                        if (q < 1 + 16 + 1 + 16 + 8 + 1) {
+                                n_active_files ++;
                                 continue;
+                        }
 
                         if (de->d_name[q-1-8-16-1] != '-' ||
-                            de->d_name[q-1-8-16-1-16-1] != '@')
+                            de->d_name[q-1-8-16-1-16-1] != '@') {
+                                n_active_files ++;
                                 continue;
+                        }
 
                         p = strdup(de->d_name);
                         if (!p) {
@@ -252,54 +254,68 @@ int journal_directory_vacuum(
 
                         if (sscanf(de->d_name + q-1-8-16-1-16, "%16llx-%16llx.journal~", &realtime, &tmp) != 2) {
                                 free(p);
+                                n_active_files ++;
                                 continue;
                         }
 
                         have_seqnum = false;
-                } else
-                        /* We do not vacuum active files or unknown files! */
+                } else {
+                        /* We do not vacuum unknown files! */
+                        log_debug("Not vacuuming unknown file %s.", de->d_name);
                         continue;
+                }
 
-                if (journal_file_empty(dirfd(d), p)) {
-                        /* Always vacuum empty non-online files. */
+                size = 512UL * (uint64_t) st.st_blocks;
 
-                        uint64_t size = 512UL * (uint64_t) st.st_blocks;
+                r = journal_file_empty(dirfd(d), p);
+                if (r < 0) {
+                        log_debug_errno(r, "Failed check if %s is empty, ignoring: %m", p);
+                        continue;
+                }
+                if (r > 0) {
+                        /* Always vacuum empty non-online files. */
 
                         if (unlinkat(dirfd(d), p, 0) >= 0) {
-                                log_full(verbose ? LOG_INFO : LOG_DEBUG, "Deleted empty archived journal %s/%s (%s).", directory, p, format_bytes(sbytes, sizeof(sbytes), size));
+
+                                log_full(verbose ? LOG_INFO : LOG_DEBUG,
+                                         "Deleted empty archived journal %s/%s (%s).", directory, p, format_bytes(sbytes, sizeof(sbytes), size));
+
                                 freed += size;
                         } else if (errno != ENOENT)
                                 log_warning_errno(errno, "Failed to delete empty archived journal %s/%s: %m", directory, p);
 
-                        free(p);
                         continue;
                 }
 
-                patch_realtime(directory, p, &st, &realtime);
+                patch_realtime(dirfd(d), p, &st, &realtime);
 
                 if (!GREEDY_REALLOC(list, n_allocated, n_list + 1)) {
-                        free(p);
                         r = -ENOMEM;
                         goto finish;
                 }
 
                 list[n_list].filename = p;
-                list[n_list].usage = 512UL * (uint64_t) st.st_blocks;
+                list[n_list].usage = size;
                 list[n_list].seqnum = seqnum;
                 list[n_list].realtime = realtime;
                 list[n_list].seqnum_id = seqnum_id;
                 list[n_list].have_seqnum = have_seqnum;
-
-                sum += list[n_list].usage;
-
                 n_list ++;
+
+                p = NULL;
+                sum += size;
         }
 
         qsort_safe(list, n_list, sizeof(struct vacuum_info), vacuum_compare);
 
         for (i = 0; i < n_list; i++) {
+                unsigned left;
+
+                left = n_active_files + n_list - i;
+
                 if ((max_retention_usec <= 0 || list[i].realtime >= retention_limit) &&
-                    (max_use <= 0 || sum <= max_use))
+                    (max_use <= 0 || sum <= max_use) &&
+                    (n_max_files <= 0 || left <= n_max_files))
                         break;
 
                 if (unlinkat(dirfd(d), list[i].filename, 0) >= 0) {
@@ -318,6 +334,8 @@ int journal_directory_vacuum(
         if (oldest_usec && i < n_list && (*oldest_usec == 0 || list[i].realtime < *oldest_usec))
                 *oldest_usec = list[i].realtime;
 
+        r = 0;
+
 finish:
         for (i = 0; i < n_list; i++)
                 free(list[i].filename);
index c45cc31d0e38222e29863395a0dc6a62207a4b5d..49ab90af9172c07af50470b8aca6719817c1c4fc 100644 (file)
@@ -21,5 +21,9 @@
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
+#include <inttypes.h>
+#include <stdbool.h>
 
-int journal_directory_vacuum(const char *directory, uint64_t max_use, usec_t max_retention_usec, usec_t *oldest_usec, bool vacuum);
+#include "time-util.h"
+
+int journal_directory_vacuum(const char *directory, uint64_t max_use, uint64_t n_max_files, usec_t max_retention_usec, usec_t *oldest_usec, bool verbose);
index 28ccb806616ec89e9ce68b21b1605b291b2e7dde..d9851db36cb4e91de59b867bd276a6bbd935d8a2 100644 (file)
@@ -106,8 +106,9 @@ static bool arg_reverse = false;
 static int arg_journal_type = 0;
 static const char *arg_root = NULL;
 static const char *arg_machine = NULL;
-static uint64_t arg_vacuum_size = (uint64_t) -1;
-static usec_t arg_vacuum_time = USEC_INFINITY;
+static uint64_t arg_vacuum_size = 0;
+static uint64_t arg_vacuum_n_files = 0;
+static usec_t arg_vacuum_time = 0;
 
 static enum {
         ACTION_SHOW,
@@ -235,7 +236,8 @@ static void help(void) {
                "     --new-id128           Generate a new 128-bit ID\n"
                "     --disk-usage          Show total disk usage of all journal files\n"
                "     --vacuum-size=BYTES   Reduce disk usage below specified size\n"
-               "     --vacuum-time=TIME    Remove journal files older than specified date\n"
+               "     --vacuum-files=INT    Leave only the specified number of journal files\n"
+               "     --vacuum-time=TIME    Remove journal files older than specified time\n"
                "     --flush               Flush all journal data from /run into /var\n"
                "     --rotate              Request immediate rotation of the journal files\n"
                "     --header              Show journal header information\n"
@@ -281,6 +283,7 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_FLUSH,
                 ARG_ROTATE,
                 ARG_VACUUM_SIZE,
+                ARG_VACUUM_FILES,
                 ARG_VACUUM_TIME,
         };
 
@@ -335,6 +338,7 @@ static int parse_argv(int argc, char *argv[]) {
                 { "flush",          no_argument,       NULL, ARG_FLUSH          },
                 { "rotate",         no_argument,       NULL, ARG_ROTATE         },
                 { "vacuum-size",    required_argument, NULL, ARG_VACUUM_SIZE    },
+                { "vacuum-files",   required_argument, NULL, ARG_VACUUM_FILES   },
                 { "vacuum-time",    required_argument, NULL, ARG_VACUUM_TIME    },
                 {}
         };
@@ -540,6 +544,16 @@ static int parse_argv(int argc, char *argv[]) {
                         arg_action = ACTION_VACUUM;
                         break;
 
+                case ARG_VACUUM_FILES:
+                        r = safe_atou64(optarg, &arg_vacuum_n_files);
+                        if (r < 0) {
+                                log_error("Failed to parse vacuum files: %s", optarg);
+                                return r;
+                        }
+
+                        arg_action = ACTION_VACUUM;
+                        break;
+
                 case ARG_VACUUM_TIME:
                         r = parse_sec(optarg, &arg_vacuum_time);
                         if (r < 0) {
@@ -1929,9 +1943,9 @@ int main(int argc, char *argv[]) {
                         if (d->is_root)
                                 continue;
 
-                        q = journal_directory_vacuum(d->path, arg_vacuum_size, arg_vacuum_time, NULL, true);
+                        q = journal_directory_vacuum(d->path, arg_vacuum_size, arg_vacuum_n_files, arg_vacuum_time, NULL, true);
                         if (q < 0) {
-                                log_error_errno(q, "Failed to vacuum: %m");
+                                log_error_errno(q, "Failed to vacuum %s: %m", d->path);
                                 r = q;
                         }
                 }
index bf7c773009ca974a3cebc50012bb479ede328132..c154610c542c9d70dd0c73343c01e95b20cbddc0 100644 (file)
@@ -24,9 +24,11 @@ Journal.RateLimitBurst,     config_parse_unsigned,   0, offsetof(Server, rate_li
 Journal.SystemMaxUse,       config_parse_iec_uint64, 0, offsetof(Server, system_metrics.max_use)
 Journal.SystemMaxFileSize,  config_parse_iec_uint64, 0, offsetof(Server, system_metrics.max_size)
 Journal.SystemKeepFree,     config_parse_iec_uint64, 0, offsetof(Server, system_metrics.keep_free)
+Journal.SystemMaxFiles,     config_parse_uint64,     0, offsetof(Server, system_metrics.n_max_files)
 Journal.RuntimeMaxUse,      config_parse_iec_uint64, 0, offsetof(Server, runtime_metrics.max_use)
 Journal.RuntimeMaxFileSize, config_parse_iec_uint64, 0, offsetof(Server, runtime_metrics.max_size)
 Journal.RuntimeKeepFree,    config_parse_iec_uint64, 0, offsetof(Server, runtime_metrics.keep_free)
+Journal.RuntimeMaxFiles,    config_parse_uint64,     0, offsetof(Server, runtime_metrics.n_max_files)
 Journal.MaxRetentionSec,    config_parse_sec,        0, offsetof(Server, max_retention_usec)
 Journal.MaxFileSec,         config_parse_sec,        0, offsetof(Server, max_file_usec)
 Journal.ForwardToSyslog,    config_parse_bool,       0, offsetof(Server, forward_to_syslog)
index 4566612949d7d59f3b27f250d2e1aec453fc57ac..fb172b7f5dada047f5deb3877b9cc361aeead17e 100644 (file)
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
-#include <sys/signalfd.h>
-#include <sys/ioctl.h>
 #include <linux/sockios.h>
-#include <sys/statvfs.h>
-#include <sys/mman.h>
-
 #ifdef HAVE_SELINUX
 #include <selinux/selinux.h>
 #endif
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/signalfd.h>
+#include <sys/statvfs.h>
 
-#include <libudev.h>
-
+#include "libudev.h"
+#include "sd-daemon.h"
 #include "sd-journal.h"
 #include "sd-messages.h"
-#include "sd-daemon.h"
-#include "mkdir.h"
-#include "rm-rf.h"
-#include "hashmap.h"
-#include "journal-file.h"
-#include "socket-util.h"
+
+#include "acl-util.h"
 #include "cgroup-util.h"
-#include "missing.h"
 #include "conf-parser.h"
-#include "selinux-util.h"
-#include "acl-util.h"
 #include "formats-util.h"
-#include "process-util.h"
+#include "hashmap.h"
 #include "hostname-util.h"
+#include "missing.h"
+#include "mkdir.h"
+#include "process-util.h"
+#include "rm-rf.h"
+#include "selinux-util.h"
 #include "signal-util.h"
+#include "socket-util.h"
+#include "journal-authenticate.h"
+#include "journal-file.h"
 #include "journal-internal.h"
 #include "journal-vacuum.h"
-#include "journal-authenticate.h"
-#include "journald-rate-limit.h"
+#include "journald-audit.h"
 #include "journald-kmsg.h"
-#include "journald-syslog.h"
-#include "journald-stream.h"
 #include "journald-native.h"
-#include "journald-audit.h"
+#include "journald-rate-limit.h"
 #include "journald-server.h"
+#include "journald-stream.h"
+#include "journald-syslog.h"
 
 #define USER_JOURNALS_MAX 1024
 
 #define DEFAULT_RATE_LIMIT_BURST 1000
 #define DEFAULT_MAX_FILE_USEC USEC_PER_MONTH
 
-#define RECHECK_AVAILABLE_SPACE_USEC (30*USEC_PER_SEC)
-
-static const char* const storage_table[_STORAGE_MAX] = {
-        [STORAGE_AUTO] = "auto",
-        [STORAGE_VOLATILE] = "volatile",
-        [STORAGE_PERSISTENT] = "persistent",
-        [STORAGE_NONE] = "none"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(storage, Storage);
-DEFINE_CONFIG_PARSE_ENUM(config_parse_storage, storage, Storage, "Failed to parse storage setting");
-
-static const char* const split_mode_table[_SPLIT_MAX] = {
-        [SPLIT_LOGIN] = "login",
-        [SPLIT_UID] = "uid",
-        [SPLIT_NONE] = "none",
-};
+#define RECHECK_SPACE_USEC (30*USEC_PER_SEC)
 
-DEFINE_STRING_TABLE_LOOKUP(split_mode, SplitMode);
-DEFINE_CONFIG_PARSE_ENUM(config_parse_split_mode, split_mode, SplitMode, "Failed to parse split mode setting");
-
-static uint64_t available_space(Server *s, bool verbose) {
-        char ids[33];
-        _cleanup_free_ char *p = NULL;
-        sd_id128_t machine;
-        struct statvfs ss;
-        uint64_t sum = 0, ss_avail = 0, avail = 0;
-        int r;
+static int determine_space_for(
+                Server *s,
+                JournalMetrics *metrics,
+                const char *path,
+                const char *name,
+                bool verbose,
+                bool patch_min_use,
+                uint64_t *available,
+                uint64_t *limit) {
+
+        uint64_t sum = 0, ss_avail, avail;
         _cleanup_closedir_ DIR *d = NULL;
+        struct dirent *de;
+        struct statvfs ss;
+        const char *p;
         usec_t ts;
-        const char *f;
-        JournalMetrics *m;
-
-        ts = now(CLOCK_MONOTONIC);
 
-        if (s->cached_available_space_timestamp + RECHECK_AVAILABLE_SPACE_USEC > ts
-            && !verbose)
-                return s->cached_available_space;
+        assert(s);
+        assert(metrics);
+        assert(path);
+        assert(name);
 
-        r = sd_id128_get_machine(&machine);
-        if (r < 0)
-                return 0;
+        ts = now(CLOCK_MONOTONIC);
 
-        if (s->system_journal) {
-                f = "/var/log/journal/";
-                m = &s->system_metrics;
-        } else {
-                f = "/run/log/journal/";
-                m = &s->runtime_metrics;
-        }
+        if (!verbose && s->cached_space_timestamp + RECHECK_SPACE_USEC > ts) {
 
-        assert(m);
+                if (available)
+                        *available = s->cached_space_available;
+                if (limit)
+                        *limit = s->cached_space_limit;
 
-        p = strappend(f, sd_id128_to_string(machine, ids));
-        if (!p)
                 return 0;
+        }
 
+        p = strjoina(path, SERVER_MACHINE_ID(s));
         d = opendir(p);
         if (!d)
-                return 0;
+                return log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_ERR, errno, "Failed to open %s: %m", p);
 
         if (fstatvfs(dirfd(d), &ss) < 0)
-                return 0;
+                return log_error_errno(errno, "Failed to fstatvfs(%s): %m", p);
 
-        for (;;) {
+        FOREACH_DIRENT_ALL(de, d, break) {
                 struct stat st;
-                struct dirent *de;
-
-                errno = 0;
-                de = readdir(d);
-                if (!de && errno != 0)
-                        return 0;
-
-                if (!de)
-                        break;
 
                 if (!endswith(de->d_name, ".journal") &&
                     !endswith(de->d_name, ".journal~"))
                         continue;
 
-                if (fstatat(dirfd(d), de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0)
+                if (fstatat(dirfd(d), de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
+                        log_debug_errno(errno, "Failed to stat %s/%s, ignoring: %m", p, de->d_name);
                         continue;
+                }
 
                 if (!S_ISREG(st.st_mode))
                         continue;
@@ -155,45 +127,72 @@ static uint64_t available_space(Server *s, bool verbose) {
                 sum += (uint64_t) st.st_blocks * 512UL;
         }
 
-        ss_avail = ss.f_bsize * ss.f_bavail;
-
-        /* If we reached a high mark, we will always allow this much
-         * again, unless usage goes above max_use. This watermark
-         * value is cached so that we don't give up space on pressure,
-         * but hover below the maximum usage. */
+        /* If request, then let's bump the min_use limit to the
+         * current usage on disk. We do this when starting up and
+         * first opening the journal files. This way sudden spikes in
+         * disk usage will not cause journald to vacuum files without
+         * bounds. Note that this means that only a restart of
+         * journald will make it reset this value. */
 
-        if (m->use < sum)
-                m->use = sum;
+        if (patch_min_use)
+                metrics->min_use = MAX(metrics->min_use, sum);
 
-        avail = LESS_BY(ss_avail, m->keep_free);
+        ss_avail = ss.f_bsize * ss.f_bavail;
+        avail = LESS_BY(ss_avail, metrics->keep_free);
 
-        s->cached_available_space = LESS_BY(MIN(m->max_use, avail), sum);
-        s->cached_available_space_timestamp = ts;
+        s->cached_space_limit = MIN(MAX(sum + avail, metrics->min_use), metrics->max_use);
+        s->cached_space_available = LESS_BY(s->cached_space_limit, sum);
+        s->cached_space_timestamp = ts;
 
         if (verbose) {
                 char    fb1[FORMAT_BYTES_MAX], fb2[FORMAT_BYTES_MAX], fb3[FORMAT_BYTES_MAX],
-                        fb4[FORMAT_BYTES_MAX], fb5[FORMAT_BYTES_MAX];
+                        fb4[FORMAT_BYTES_MAX], fb5[FORMAT_BYTES_MAX], fb6[FORMAT_BYTES_MAX];
 
                 server_driver_message(s, SD_MESSAGE_JOURNAL_USAGE,
-                                      "%s is currently using %s.\n"
+                                      "%s (%s) is currently using %s.\n"
                                       "Maximum allowed usage is set to %s.\n"
                                       "Leaving at least %s free (of currently available %s of space).\n"
-                                      "Enforced usage limit is thus %s.",
-                                      s->system_journal ? "Permanent journal (/var/log/journal/)" : "Runtime journal (/run/log/journal/)",
+                                      "Enforced usage limit is thus %s, of which %s are still available.",
+                                      name, path,
                                       format_bytes(fb1, sizeof(fb1), sum),
-                                      format_bytes(fb2, sizeof(fb2), m->max_use),
-                                      format_bytes(fb3, sizeof(fb3), m->keep_free),
+                                      format_bytes(fb2, sizeof(fb2), metrics->max_use),
+                                      format_bytes(fb3, sizeof(fb3), metrics->keep_free),
                                       format_bytes(fb4, sizeof(fb4), ss_avail),
-                                      format_bytes(fb5, sizeof(fb5), s->cached_available_space + sum));
+                                      format_bytes(fb5, sizeof(fb5), s->cached_space_limit),
+                                      format_bytes(fb6, sizeof(fb6), s->cached_space_available));
+        }
+
+        if (available)
+                *available = s->cached_space_available;
+        if (limit)
+                *limit = s->cached_space_limit;
+
+        return 1;
+}
+
+static int determine_space(Server *s, bool verbose, bool patch_min_use, uint64_t *available, uint64_t *limit) {
+        JournalMetrics *metrics;
+        const char *path, *name;
+
+        assert(s);
+
+        if (s->system_journal) {
+                path = "/var/log/journal/";
+                metrics = &s->system_metrics;
+                name = "System journal";
+        } else {
+                path = "/run/log/journal/";
+                metrics = &s->runtime_metrics;
+                name = "Runtime journal";
         }
 
-        return s->cached_available_space;
+        return determine_space_for(s, metrics, path, name, verbose, patch_min_use, available, limit);
 }
 
 void server_fix_perms(Server *s, JournalFile *f, uid_t uid) {
         int r;
 #ifdef HAVE_ACL
-        acl_t acl;
+        _cleanup_(acl_freep) acl_t acl = NULL;
         acl_entry_t entry;
         acl_permset_t permset;
 #endif
@@ -202,7 +201,7 @@ void server_fix_perms(Server *s, JournalFile *f, uid_t uid) {
 
         r = fchmod(f->fd, 0640);
         if (r < 0)
-                log_warning_errno(r, "Failed to fix access mode on %s, ignoring: %m", f->path);
+                log_warning_errno(errno, "Failed to fix access mode on %s, ignoring: %m", f->path);
 
 #ifdef HAVE_ACL
         if (uid <= SYSTEM_UID_MAX)
@@ -221,7 +220,7 @@ void server_fix_perms(Server *s, JournalFile *f, uid_t uid) {
                     acl_set_tag_type(entry, ACL_USER) < 0 ||
                     acl_set_qualifier(entry, &uid) < 0) {
                         log_warning_errno(errno, "Failed to patch ACL on %s, ignoring: %m", f->path);
-                        goto finish;
+                        return;
                 }
         }
 
@@ -231,14 +230,12 @@ void server_fix_perms(Server *s, JournalFile *f, uid_t uid) {
             acl_add_perm(permset, ACL_READ) < 0 ||
             calc_acl_mask_if_needed(&acl) < 0) {
                 log_warning_errno(errno, "Failed to patch ACL on %s, ignoring: %m", f->path);
-                goto finish;
+                return;
         }
 
         if (acl_set_fd(f->fd, acl) < 0)
                 log_warning_errno(errno, "Failed to set ACL on %s, ignoring: %m", f->path);
 
-finish:
-        acl_free(acl);
 #endif
 }
 
@@ -328,8 +325,8 @@ void server_rotate(Server *s) {
 
         log_debug("Rotating...");
 
-        do_rotate(s, &s->runtime_journal, "runtime", false, 0);
-        do_rotate(s, &s->system_journal, "system", s->seal, 0);
+        (void) do_rotate(s, &s->runtime_journal, "runtime", false, 0);
+        (void) do_rotate(s, &s->system_journal, "system", s->seal, 0);
 
         ORDERED_HASHMAP_FOREACH_KEY(f, k, s->user_journals, i) {
                 r = do_rotate(s, &f, "user", s->seal, PTR_TO_UINT32(k));
@@ -350,13 +347,13 @@ void server_sync(Server *s) {
         if (s->system_journal) {
                 r = journal_file_set_offline(s->system_journal);
                 if (r < 0)
-                        log_error_errno(r, "Failed to sync system journal: %m");
+                        log_warning_errno(r, "Failed to sync system journal, ignoring: %m");
         }
 
         ORDERED_HASHMAP_FOREACH_KEY(f, k, s->user_journals, i) {
                 r = journal_file_set_offline(f);
                 if (r < 0)
-                        log_error_errno(r, "Failed to sync user journal: %m");
+                        log_warning_errno(r, "Failed to sync user journal, ignoring: %m");
         }
 
         if (s->sync_event_source) {
@@ -370,43 +367,50 @@ void server_sync(Server *s) {
 
 static void do_vacuum(
                 Server *s,
-                const char *id,
                 JournalFile *f,
-                const char* path,
-                JournalMetrics *metrics) {
+                JournalMetrics *metrics,
+                const char *path,
+                const char *name,
+                bool verbose,
+                bool patch_min_use) {
 
         const char *p;
+        uint64_t limit;
         int r;
 
+        assert(s);
+        assert(metrics);
+        assert(path);
+        assert(name);
+
         if (!f)
                 return;
 
-        p = strjoina(path, id);
-        r = journal_directory_vacuum(p, metrics->max_use, s->max_retention_usec, &s->oldest_file_usec, false);
+        p = strjoina(path, SERVER_MACHINE_ID(s));
+
+        limit = metrics->max_use;
+        (void) determine_space_for(s, metrics, path, name, verbose, patch_min_use, NULL, &limit);
+
+        r = journal_directory_vacuum(p, limit, metrics->n_max_files, s->max_retention_usec, &s->oldest_file_usec,  verbose);
         if (r < 0 && r != -ENOENT)
-                log_error_errno(r, "Failed to vacuum %s: %m", p);
+                log_warning_errno(r, "Failed to vacuum %s, ignoring: %m", p);
 }
 
-void server_vacuum(Server *s) {
-        char ids[33];
-        sd_id128_t machine;
-        int r;
+int server_vacuum(Server *s, bool verbose, bool patch_min_use) {
+        assert(s);
 
         log_debug("Vacuuming...");
 
         s->oldest_file_usec = 0;
 
-        r = sd_id128_get_machine(&machine);
-        if (r < 0) {
-                log_error_errno(r, "Failed to get machine ID: %m");
-                return;
-        }
-        sd_id128_to_string(machine, ids);
+        do_vacuum(s, s->system_journal, &s->system_metrics, "/var/log/journal/", "System journal", verbose, patch_min_use);
+        do_vacuum(s, s->runtime_journal, &s->runtime_metrics, "/run/log/journal/", "Runtime journal", verbose, patch_min_use);
 
-        do_vacuum(s, ids, s->system_journal, "/var/log/journal/", &s->system_metrics);
-        do_vacuum(s, ids, s->runtime_journal, "/run/log/journal/", &s->runtime_metrics);
+        s->cached_space_limit = 0;
+        s->cached_space_available = 0;
+        s->cached_space_timestamp = 0;
 
-        s->cached_available_space_timestamp = 0;
+        return 0;
 }
 
 static void server_cache_machine_id(Server *s) {
@@ -504,7 +508,7 @@ static void write_to_journal(Server *s, uid_t uid, struct iovec *iovec, unsigned
         if (journal_file_rotate_suggested(f, s->max_file_usec)) {
                 log_debug("%s: Journal header limits reached or header out-of-date, rotating.", f->path);
                 server_rotate(s);
-                server_vacuum(s);
+                server_vacuum(s, false, false);
                 vacuumed = true;
 
                 f = find_journal(s, uid);
@@ -524,7 +528,7 @@ static void write_to_journal(Server *s, uid_t uid, struct iovec *iovec, unsigned
         }
 
         server_rotate(s);
-        server_vacuum(s);
+        server_vacuum(s, false, false);
 
         f = find_journal(s, uid);
         if (!f)
@@ -869,6 +873,7 @@ void server_dispatch_message(
 
         int rl, r;
         _cleanup_free_ char *path = NULL;
+        uint64_t available = 0;
         char *c;
 
         assert(s);
@@ -908,9 +913,8 @@ void server_dispatch_message(
                 }
         }
 
-        rl = journal_rate_limit_test(s->rate_limit, path,
-                                     priority & LOG_PRIMASK, available_space(s, false));
-
+        (void) determine_space(s, false, false, &available, NULL);
+        rl = journal_rate_limit_test(s->rate_limit, path, priority & LOG_PRIMASK, available);
         if (rl == 0)
                 return;
 
@@ -925,16 +929,8 @@ finish:
 
 
 static int system_journal_open(Server *s, bool flush_requested) {
+        const char *fn;
         int r;
-        char *fn;
-        sd_id128_t machine;
-        char ids[33];
-
-        r = sd_id128_get_machine(&machine);
-        if (r < 0)
-                return log_error_errno(r, "Failed to get machine id: %m");
-
-        sd_id128_to_string(machine, ids);
 
         if (!s->system_journal &&
             (s->storage == STORAGE_PERSISTENT || s->storage == STORAGE_AUTO) &&
@@ -950,15 +946,15 @@ static int system_journal_open(Server *s, bool flush_requested) {
                 if (s->storage == STORAGE_PERSISTENT)
                         (void) mkdir_p("/var/log/journal/", 0755);
 
-                fn = strjoina("/var/log/journal/", ids);
+                fn = strjoina("/var/log/journal/", SERVER_MACHINE_ID(s));
                 (void) mkdir(fn, 0755);
 
                 fn = strjoina(fn, "/system.journal");
                 r = journal_file_open_reliably(fn, O_RDWR|O_CREAT, 0640, s->compress, s->seal, &s->system_metrics, s->mmap, NULL, &s->system_journal);
-
-                if (r >= 0)
+                if (r >= 0) {
                         server_fix_perms(s, s->system_journal, 0);
-                else if (r < 0) {
+                        (void) determine_space_for(s, &s->system_metrics, "/var/log/journal/", "System journal", true, true, NULL, NULL);
+                } else if (r < 0) {
                         if (r != -ENOENT && r != -EROFS)
                                 log_warning_errno(r, "Failed to open system journal: %m");
 
@@ -969,9 +965,7 @@ static int system_journal_open(Server *s, bool flush_requested) {
         if (!s->runtime_journal &&
             (s->storage != STORAGE_NONE)) {
 
-                fn = strjoin("/run/log/journal/", ids, "/system.journal", NULL);
-                if (!fn)
-                        return -ENOMEM;
+                fn = strjoina("/run/log/journal/", SERVER_MACHINE_ID(s), "/system.journal");
 
                 if (s->system_journal) {
 
@@ -980,8 +974,6 @@ static int system_journal_open(Server *s, bool flush_requested) {
                          * it into the system journal */
 
                         r = journal_file_open(fn, O_RDWR, 0640, s->compress, false, &s->runtime_metrics, s->mmap, NULL, &s->runtime_journal);
-                        free(fn);
-
                         if (r < 0) {
                                 if (r != -ENOENT)
                                         log_warning_errno(r, "Failed to open runtime journal: %m");
@@ -999,18 +991,16 @@ static int system_journal_open(Server *s, bool flush_requested) {
                         (void) mkdir_parents(fn, 0750);
 
                         r = journal_file_open_reliably(fn, O_RDWR|O_CREAT, 0640, s->compress, false, &s->runtime_metrics, s->mmap, NULL, &s->runtime_journal);
-                        free(fn);
-
                         if (r < 0)
                                 return log_error_errno(r, "Failed to open runtime journal: %m");
                 }
 
-                if (s->runtime_journal)
+                if (s->runtime_journal) {
                         server_fix_perms(s, s->runtime_journal, 0);
+                        (void) determine_space_for(s, &s->runtime_metrics, "/run/log/journal/", "Runtime journal", true, true, NULL, NULL);
+                }
         }
 
-        available_space(s, true);
-
         return r;
 }
 
@@ -1031,7 +1021,7 @@ int server_flush_to_var(Server *s) {
         if (!s->runtime_journal)
                 return 0;
 
-        system_journal_open(s, true);
+        (void) system_journal_open(s, true);
 
         if (!s->system_journal)
                 return 0;
@@ -1075,7 +1065,7 @@ int server_flush_to_var(Server *s) {
                 }
 
                 server_rotate(s);
-                server_vacuum(s);
+                server_vacuum(s, false, false);
 
                 if (!s->system_journal) {
                         log_notice("Didn't flush runtime journal since rotation of system journal wasn't successful.");
@@ -1091,11 +1081,12 @@ int server_flush_to_var(Server *s) {
                 }
         }
 
+        r = 0;
+
 finish:
         journal_file_post_change(s->system_journal);
 
-        journal_file_close(s->runtime_journal);
-        s->runtime_journal = NULL;
+        s->runtime_journal = journal_file_close(s->runtime_journal);
 
         if (r >= 0)
                 (void) rm_rf("/run/log/journal", REMOVE_ROOT);
@@ -1238,7 +1229,7 @@ static int dispatch_sigusr1(sd_event_source *es, const struct signalfd_siginfo *
 
         server_flush_to_var(s);
         server_sync(s);
-        server_vacuum(s);
+        server_vacuum(s, false, false);
 
         touch("/run/systemd/journal/flushed");
 
@@ -1252,7 +1243,7 @@ static int dispatch_sigusr2(sd_event_source *es, const struct signalfd_siginfo *
 
         log_info("Received request to rotate journal from PID %"PRIu32, si->ssi_pid);
         server_rotate(s);
-        server_vacuum(s);
+        server_vacuum(s, true, true);
 
         return 0;
 }
@@ -1340,8 +1331,8 @@ static int server_parse_proc_cmdline(Server *s) {
                 } else if (startswith(word, "systemd.journald"))
                         log_warning("Invalid systemd.journald parameter. Ignoring.");
         }
-        /* do not warn about state here, since probably systemd already did */
 
+        /* do not warn about state here, since probably systemd already did */
         return 0;
 }
 
@@ -1479,18 +1470,19 @@ int server_init(Server *s) {
         s->max_level_console = LOG_INFO;
         s->max_level_wall = LOG_EMERG;
 
-        memset(&s->system_metrics, 0xFF, sizeof(s->system_metrics));
-        memset(&s->runtime_metrics, 0xFF, sizeof(s->runtime_metrics));
+        journal_reset_metrics(&s->system_metrics);
+        journal_reset_metrics(&s->runtime_metrics);
 
         server_parse_config_file(s);
         server_parse_proc_cmdline(s);
+
         if (!!s->rate_limit_interval ^ !!s->rate_limit_burst) {
                 log_debug("Setting both rate limit interval and burst from "USEC_FMT",%u to 0,0",
                           s->rate_limit_interval, s->rate_limit_burst);
                 s->rate_limit_interval = s->rate_limit_burst = 0;
         }
 
-        mkdir_p("/run/systemd/journal", 0755);
+        (void) mkdir_p("/run/systemd/journal", 0755);
 
         s->user_journals = ordered_hashmap_new(NULL);
         if (!s->user_journals)
@@ -1616,11 +1608,7 @@ int server_init(Server *s) {
         server_cache_boot_id(s);
         server_cache_machine_id(s);
 
-        r = system_journal_open(s, false);
-        if (r < 0)
-                return r;
-
-        return 0;
+        return system_journal_open(s, false);
 }
 
 void server_maybe_append_tags(Server *s) {
@@ -1693,3 +1681,22 @@ void server_done(Server *s) {
 
         udev_unref(s->udev);
 }
+
+static const char* const storage_table[_STORAGE_MAX] = {
+        [STORAGE_AUTO] = "auto",
+        [STORAGE_VOLATILE] = "volatile",
+        [STORAGE_PERSISTENT] = "persistent",
+        [STORAGE_NONE] = "none"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(storage, Storage);
+DEFINE_CONFIG_PARSE_ENUM(config_parse_storage, storage, Storage, "Failed to parse storage setting");
+
+static const char* const split_mode_table[_SPLIT_MAX] = {
+        [SPLIT_LOGIN] = "login",
+        [SPLIT_UID] = "uid",
+        [SPLIT_NONE] = "none",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(split_mode, SplitMode);
+DEFINE_CONFIG_PARSE_ENUM(config_parse_split_mode, split_mode, SplitMode, "Failed to parse split mode setting");
index d954c5190de724c6f0931e5a8d17d41ede35fe46..535c0ab9ab25525f70ca4b4c9720f313dc8eda18 100644 (file)
@@ -100,8 +100,9 @@ typedef struct Server {
         unsigned n_forward_syslog_missed;
         usec_t last_warn_forward_syslog_missed;
 
-        uint64_t cached_available_space;
-        usec_t cached_available_space_timestamp;
+        uint64_t cached_space_available;
+        uint64_t cached_space_limit;
+        usec_t cached_space_timestamp;
 
         uint64_t var_available_timestamp;
 
@@ -141,6 +142,8 @@ typedef struct Server {
         char *cgroup_root;
 } Server;
 
+#define SERVER_MACHINE_ID(s) ((s)->machine_id_field + strlen("_MACHINE_ID="))
+
 #define N_IOVEC_META_FIELDS 20
 #define N_IOVEC_KERNEL_FIELDS 64
 #define N_IOVEC_UDEV_FIELDS 32
@@ -166,7 +169,7 @@ void server_fix_perms(Server *s, JournalFile *f, uid_t uid);
 int server_init(Server *s);
 void server_done(Server *s);
 void server_sync(Server *s);
-void server_vacuum(Server *s);
+int server_vacuum(Server *s, bool verbose, bool patch_min_use);
 void server_rotate(Server *s);
 int server_schedule_sync(Server *s, int priority);
 int server_flush_to_var(Server *s);
index b2624c6d28a0f2e1112eaa4b24d194def8c9e173..83236ceba9b46cc653784ac4638686be7a6eff4d 100644 (file)
@@ -21,8 +21,8 @@
 
 #include <unistd.h>
 
-#include "systemd/sd-messages.h"
-#include "systemd/sd-daemon.h"
+#include "sd-messages.h"
+#include "sd-daemon.h"
 
 #include "journal-authenticate.h"
 #include "journald-server.h"
@@ -54,7 +54,7 @@ int main(int argc, char *argv[]) {
         if (r < 0)
                 goto finish;
 
-        server_vacuum(&server);
+        server_vacuum(&server, false, false);
         server_flush_to_var(&server);
         server_flush_dev_kmsg(&server);
 
@@ -82,7 +82,7 @@ int main(int argc, char *argv[]) {
                         if (server.oldest_file_usec + server.max_retention_usec < n) {
                                 log_info("Retention time reached.");
                                 server_rotate(&server);
-                                server_vacuum(&server);
+                                server_vacuum(&server, false, false);
                                 continue;
                         }
 
index 47eefe91c1501fa1bad93f9a371ce81955faa30c..7beb96c6719939f80c8f2d254a7476975411becf 100644 (file)
 #SystemMaxUse=
 #SystemKeepFree=
 #SystemMaxFileSize=
+#SystemMaxFiles=100
 #RuntimeMaxUse=
 #RuntimeKeepFree=
 #RuntimeMaxFileSize=
+#RuntimeMaxFiles=100
 #MaxRetentionSec=
 #MaxFileSec=1month
 #ForwardToSyslog=no
index adefa1b0265012c1ac0a2eb12a62605edb254727..8069339c1f485c7956b8980ece9d58371940343e 100644 (file)
@@ -197,7 +197,7 @@ static void test_skip(void (*setup)(void)) {
         if (arg_keep)
                 log_info("Not removing %s", t);
         else {
-                journal_directory_vacuum(".", 3000000, 0, NULL, true);
+                journal_directory_vacuum(".", 3000000, 0, 0, NULL, true);
 
                 assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
         }
@@ -282,7 +282,7 @@ static void test_sequence_numbers(void) {
         if (arg_keep)
                 log_info("Not removing %s", t);
         else {
-                journal_directory_vacuum(".", 3000000, 0, NULL, true);
+                journal_directory_vacuum(".", 3000000, 0, 0, NULL, true);
 
                 assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
         }
index caaab258c9bb74e2e39e63ff36b2f6c74bed8a76..01d4bc968a247936b915c3729637dbaf160b05ab 100644 (file)
@@ -116,7 +116,7 @@ static void test_non_empty(void) {
         if (arg_keep)
                 log_info("Not removing %s", t);
         else {
-                journal_directory_vacuum(".", 3000000, 0, NULL, true);
+                journal_directory_vacuum(".", 3000000, 0, 0, NULL, true);
 
                 assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
         }
@@ -155,7 +155,7 @@ static void test_empty(void) {
         if (arg_keep)
                 log_info("Not removing %s", t);
         else {
-                journal_directory_vacuum(".", 3000000, 0, NULL, true);
+                journal_directory_vacuum(".", 3000000, 0, 0, NULL, true);
 
                 assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
         }
index 3c04898e92885f473f76b33732793b1ea1f5e23f..4012cd483b3b0aae94551e8c1cc2eca4234d07f4 100644 (file)
@@ -21,6 +21,7 @@
 ***/
 
 #include "lldp-internal.h"
+#include "sd-lldp.h"
 
 /* We store maximum 1K chassis entries */
 #define LLDP_MIB_MAX_CHASSIS 1024
 /* Maximum Ports can be attached to any chassis */
 #define LLDP_MIB_MAX_PORT_PER_CHASSIS 32
 
-int lldp_read_chassis_id(tlv_packet *tlv,
-                         uint8_t *type,
-                         uint16_t *length,
-                         uint8_t **data) {
-        uint8_t subtype;
-        int r;
-
-        assert_return(tlv, -EINVAL);
-
-        r = lldp_tlv_packet_enter_container(tlv, LLDP_TYPE_CHASSIS_ID);
-        if (r < 0)
-                goto out2;
-
-        r = tlv_packet_read_u8(tlv, &subtype);
-        if (r < 0)
-                goto out1;
-
-        switch (subtype) {
-        case LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS:
-
-                r = tlv_packet_read_bytes(tlv, data, length);
-                if (r < 0)
-                        goto out1;
-
-                break;
-        default:
-                r = -EOPNOTSUPP;
-                break;
-        }
-
-        *type = subtype;
-
- out1:
-        (void) lldp_tlv_packet_exit_container(tlv);
-
- out2:
-        return r;
-}
-
-int lldp_read_port_id(tlv_packet *tlv,
-                      uint8_t *type,
-                      uint16_t *length,
-                      uint8_t **data) {
-        uint8_t subtype;
-        char *s;
-        int r;
-
-        assert_return(tlv, -EINVAL);
-
-        r = lldp_tlv_packet_enter_container(tlv, LLDP_TYPE_PORT_ID);
-        if (r < 0)
-                goto out2;
-
-        r = tlv_packet_read_u8(tlv, &subtype);
-        if (r < 0)
-                goto out1;
-
-        switch (subtype) {
-        case LLDP_PORT_SUBTYPE_PORT_COMPONENT:
-        case LLDP_PORT_SUBTYPE_INTERFACE_ALIAS:
-        case LLDP_PORT_SUBTYPE_INTERFACE_NAME:
-        case LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED:
-
-                r = tlv_packet_read_string(tlv, &s, length);
-                if (r < 0)
-                        goto out1;
-
-                *data = (uint8_t *) s;
-
-                break;
-        case LLDP_PORT_SUBTYPE_MAC_ADDRESS:
-
-                r = tlv_packet_read_bytes(tlv, data, length);
-                if (r < 0)
-                        goto out1;
-
-                break;
-        default:
-                r = -EOPNOTSUPP;
-                break;
-        }
-
-        *type = subtype;
-
- out1:
-        (void) lldp_tlv_packet_exit_container(tlv);
-
- out2:
-        return r;
-}
-
-int lldp_read_ttl(tlv_packet *tlv, uint16_t *ttl) {
-        int r;
-
-        assert_return(tlv, -EINVAL);
-
-        r = lldp_tlv_packet_enter_container(tlv, LLDP_TYPE_TTL);
-        if (r < 0)
-                goto out;
-
-        r = tlv_packet_read_u16(tlv, ttl);
-
-        (void) lldp_tlv_packet_exit_container(tlv);
-
- out:
-        return r;
-}
-
-int lldp_read_system_name(tlv_packet *tlv,
-                          uint16_t *length,
-                          char **data) {
-        char *s;
-        int r;
-
-        assert_return(tlv, -EINVAL);
-
-        r = lldp_tlv_packet_enter_container(tlv, LLDP_TYPE_SYSTEM_NAME);
-        if (r < 0)
-                return r;
-
-        r = tlv_packet_read_string(tlv, &s, length);
-        if (r < 0)
-                goto out;
-
-        *data = (char *) s;
-
- out:
-        (void) lldp_tlv_packet_exit_container(tlv);
-
-        return r;
-}
-
-int lldp_read_system_description(tlv_packet *tlv,
-                                 uint16_t *length,
-                                 char **data) {
-        char *s;
-        int r;
-
-        assert_return(tlv, -EINVAL);
-
-        r = lldp_tlv_packet_enter_container(tlv, LLDP_TYPE_SYSTEM_DESCRIPTION);
-        if (r < 0)
-                return r;
-
-        r = tlv_packet_read_string(tlv, &s, length);
-        if (r < 0)
-                goto out;
-
-        *data = (char *) s;
-
- out:
-        (void) lldp_tlv_packet_exit_container(tlv);
-
-        return r;
-}
-
-int lldp_read_port_description(tlv_packet *tlv,
-                               uint16_t *length,
-                               char **data) {
-        char *s;
-        int r;
-
-        assert_return(tlv, -EINVAL);
-
-        r = lldp_tlv_packet_enter_container(tlv, LLDP_TYPE_PORT_DESCRIPTION);
-        if (r < 0)
-                return r;
-
-        r = tlv_packet_read_string(tlv, &s, length);
-        if (r < 0)
-                goto out;
-
-        *data = (char *) s;
-
- out:
-        (void) lldp_tlv_packet_exit_container(tlv);
-
-        return r;
-}
-
-int lldp_read_system_capability(tlv_packet *tlv, uint16_t *data) {
-        int r;
-
-        assert_return(tlv, -EINVAL);
-
-        r = lldp_tlv_packet_enter_container(tlv, LLDP_TYPE_SYSTEM_CAPABILITIES);
-        if (r < 0)
-                return r;
-
-        r = tlv_packet_read_u16(tlv, data);
-        if (r < 0)
-                goto out;
-
-        return 0;
- out:
-
-        (void) lldp_tlv_packet_exit_container(tlv);
-
-        return r;
-}
-
 /* 10.5.5.2.2 mibUpdateObjects ()
  * The mibUpdateObjects () procedure updates the MIB objects corresponding to
  * the TLVs contained in the received LLDPDU for the LLDP remote system
@@ -244,7 +44,7 @@ int lldp_mib_update_objects(lldp_chassis *c, tlv_packet *tlv) {
         assert_return(c, -EINVAL);
         assert_return(tlv, -EINVAL);
 
-        r = lldp_read_port_id(tlv, &type, &length, &data);
+        r = sd_lldp_packet_read_port_id(tlv, &type, &data, &length);
         if (r < 0)
                 return r;
 
@@ -253,13 +53,13 @@ int lldp_mib_update_objects(lldp_chassis *c, tlv_packet *tlv) {
 
                 if ((p->type == type && p->length == length && !memcmp(p->data, data, p->length))) {
 
-                        r = lldp_read_ttl(tlv, &ttl);
+                        r = sd_lldp_packet_read_ttl(tlv, &ttl);
                         if (r < 0)
                                 return r;
 
                         p->until = ttl * USEC_PER_SEC + now(clock_boottime_or_monotonic());
 
-                        tlv_packet_free(p->packet);
+                        sd_lldp_packet_unref(p->packet);
                         p->packet = tlv;
 
                         prioq_reshuffle(p->c->by_expiry, p, &p->prioq_idx);
@@ -281,7 +81,7 @@ int lldp_mib_remove_objects(lldp_chassis *c, tlv_packet *tlv) {
         assert_return(c, -EINVAL);
         assert_return(tlv, -EINVAL);
 
-        r = lldp_read_port_id(tlv, &type, &length, &data);
+        r = sd_lldp_packet_read_port_id(tlv, &type, &data, &length);
         if (r < 0)
                 return r;
 
@@ -312,11 +112,11 @@ int lldp_mib_add_objects(Prioq *by_expiry,
         assert_return(neighbour_mib, -EINVAL);
         assert_return(tlv, -EINVAL);
 
-        r = lldp_read_chassis_id(tlv, &subtype, &length, &data);
+        r = sd_lldp_packet_read_chassis_id(tlv, &subtype, &data, &length);
         if (r < 0)
                 goto drop;
 
-        r = lldp_read_ttl(tlv, &ttl);
+        r = sd_lldp_packet_read_ttl(tlv, &ttl);
         if (r < 0)
                 goto drop;
 
@@ -401,7 +201,7 @@ int lldp_mib_add_objects(Prioq *by_expiry,
         return 0;
 
  drop:
-        tlv_packet_free(tlv);
+        sd_lldp_packet_unref(tlv);
 
         if (new_chassis)
                 hashmap_remove(neighbour_mib, &c->chassis_id);
@@ -435,7 +235,7 @@ void lldp_neighbour_port_free(lldp_neighbour_port *p) {
         if(!p)
                 return;
 
-        tlv_packet_free(p->packet);
+        sd_lldp_packet_unref(p->packet);
 
         free(p->data);
         free(p);
@@ -452,11 +252,11 @@ int lldp_neighbour_port_new(lldp_chassis *c,
 
         assert(tlv);
 
-        r = lldp_read_port_id(tlv, &type, &length, &data);
+        r = sd_lldp_packet_read_port_id(tlv, &type, &data, &length);
         if (r < 0)
                 return r;
 
-        r = lldp_read_ttl(tlv, &ttl);
+        r = sd_lldp_packet_read_ttl(tlv, &ttl);
         if (r < 0)
                 return r;
 
@@ -505,7 +305,7 @@ int lldp_chassis_new(tlv_packet *tlv,
 
         assert(tlv);
 
-        r = lldp_read_chassis_id(tlv, &type, &length, &data);
+        r = sd_lldp_packet_read_chassis_id(tlv, &type, &data, &length);
         if (r < 0)
                 return r;
 
@@ -531,3 +331,30 @@ int lldp_chassis_new(tlv_packet *tlv,
 
         return 0;
 }
+
+int lldp_receive_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
+        _cleanup_lldp_packet_unref_ tlv_packet *packet = NULL;
+        tlv_packet *p;
+        uint16_t length;
+        int r;
+
+        assert(fd);
+        assert(userdata);
+
+        r = tlv_packet_new(&packet);
+        if (r < 0)
+                return r;
+
+        length = read(fd, &packet->pdu, sizeof(packet->pdu));
+
+        /* Silently drop the packet */
+        if ((size_t) length > ETHER_MAX_LEN)
+                return 0;
+
+        packet->userdata = userdata;
+
+        p = packet;
+        packet = NULL;
+
+        return lldp_handle_packet(p, (uint16_t) length);
+}
index f4eadbb87e6ad34949d69d830558c8fc97782a21..284cc6720e2db0ee5fa06ef82a2f3cb598fba0ba 100644 (file)
@@ -26,6 +26,7 @@
 #include "list.h"
 #include "lldp-tlv.h"
 #include "prioq.h"
+#include "sd-event.h"
 
 typedef struct lldp_neighbour_port lldp_neighbour_port;
 typedef struct lldp_chassis lldp_chassis;
@@ -86,13 +87,6 @@ int lldp_mib_update_objects(lldp_chassis *c, tlv_packet *tlv);
 int lldp_mib_add_objects(Prioq *by_expiry, Hashmap *neighbour_mib, tlv_packet *tlv);
 int lldp_mib_remove_objects(lldp_chassis *c, tlv_packet *tlv);
 
-int lldp_read_chassis_id(tlv_packet *tlv, uint8_t *type, uint16_t *length, uint8_t **data);
-int lldp_read_port_id(tlv_packet *tlv, uint8_t *type, uint16_t *length, uint8_t **data);
-int lldp_read_ttl(tlv_packet *tlv, uint16_t *ttl);
-int lldp_read_system_name(tlv_packet *tlv, uint16_t *length, char **data);
-int lldp_read_system_description(tlv_packet *tlv, uint16_t *length, char **data);
-int lldp_read_system_capability(tlv_packet *tlv, uint16_t *data);
-int lldp_read_port_description(tlv_packet *tlv, uint16_t *length, char **data);
-
 int lldp_handle_packet(tlv_packet *m, uint16_t length);
+int lldp_receive_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata);
 #define log_lldp(fmt, ...) log_internal(LOG_DEBUG, 0, __FILE__, __LINE__, __func__, "LLDP: " fmt, ##__VA_ARGS__)
index 664d2f7867dabe0f19eba90a21f554409987e734..12a6599ff1db8ddbd5793d5707068de4a6963e97 100644 (file)
@@ -82,30 +82,3 @@ int lldp_network_bind_raw_socket(int ifindex) {
 
         return r;
 }
-
-int lldp_receive_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
-        _cleanup_tlv_packet_free_ tlv_packet *packet = NULL;
-        tlv_packet *p;
-        uint16_t length;
-        int r;
-
-        assert(fd);
-        assert(userdata);
-
-        r = tlv_packet_new(&packet);
-        if (r < 0)
-                return r;
-
-        length = read(fd, &packet->pdu, sizeof(packet->pdu));
-
-        /* Silently drop the packet */
-        if ((size_t) length > ETHER_MAX_LEN)
-                return 0;
-
-        packet->userdata = userdata;
-
-        p = packet;
-        packet = NULL;
-
-        return lldp_handle_packet(p, (uint16_t) length);
-}
index b7f8d3bf80cc4da5de543cbb5e6a439428149f64..74ee13a4147baf6d13226f344f48d0862aa580b6 100644 (file)
@@ -25,4 +25,3 @@
 #include "sd-event.h"
 
 int lldp_network_bind_raw_socket(int ifindex);
-int lldp_receive_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata);
index 97fe7c1dd396dc5b77d4153a59f039771e755cad..7486b4c38f24d83cb4dd1bef8cf915f7cfaaddea 100644 (file)
@@ -23,6 +23,7 @@
 #include "async.h"
 #include "lldp-port.h"
 #include "lldp-network.h"
+#include "lldp-internal.h"
 
 int lldp_port_start(lldp_port *p) {
         int r;
index 0cea5b10a6ef791614fb361fc036e6365b8ddb05..66af22e37dd7fd64a8dffb4fdf2980eb176589fb 100644 (file)
@@ -54,22 +54,41 @@ int tlv_packet_new(tlv_packet **ret) {
                 return -ENOMEM;
 
         LIST_HEAD_INIT(m->sections);
+        m->n_ref = 1;
 
         *ret = m;
 
         return 0;
 }
 
-void tlv_packet_free(tlv_packet *m) {
+tlv_packet *sd_lldp_packet_ref(tlv_packet *m) {
+
+        if (!m)
+                return NULL;
+
+        assert(m->n_ref > 0);
+        m->n_ref++;
+
+        return m;
+}
+
+tlv_packet *sd_lldp_packet_unref(tlv_packet *m) {
         tlv_section *s, *n;
 
         if (!m)
-                return;
+                return NULL;
+
+        assert(m->n_ref > 0);
+        m->n_ref--;
+
+        if (m->n_ref > 0)
+                return m;
 
         LIST_FOREACH_SAFE(section, s, n, m->sections)
                 tlv_section_free(s);
 
         free(m);
+        return NULL;
 }
 
 int tlv_packet_append_bytes(tlv_packet *m, const void *data, size_t data_length) {
@@ -221,9 +240,9 @@ int tlv_packet_read_string(tlv_packet *m, char **data, uint16_t *data_length) {
                 return r;
 
         *data = (char *) val;
-        *data_length = m->container->length;
+        *data_length = m->container->data + m->container->length - m->container->read_pos;
 
-        m->container->read_pos += m->container->length;
+        m->container->read_pos += *data_length;
 
         return 0;
 }
@@ -239,9 +258,9 @@ int tlv_packet_read_bytes(tlv_packet *m, uint8_t **data, uint16_t *data_length)
                 return r;
 
         *data = (uint8_t *) val;
-        *data_length = m->container->length;
+        *data_length = m->container->data + m->container->length - m->container->read_pos;
 
-        m->container->read_pos += m->container->length;
+        m->container->read_pos += *data_length;
 
         return 0;
 }
@@ -258,7 +277,7 @@ int tlv_packet_parse_pdu(tlv_packet *m, uint16_t size) {
 
         p = m->pdu;
 
-        /* extract ethernet herader */
+        /* extract ethernet header */
         memcpy(&m->mac, p, ETH_ALEN);
         p += sizeof(struct ether_header);
 
@@ -278,6 +297,17 @@ int tlv_packet_parse_pdu(tlv_packet *m, uint16_t size) {
                 }
 
                 p += 2;
+
+                if (section->type == LLDP_TYPE_PRIVATE &&
+                    section->length >= LLDP_OUI_LEN + 1) {
+                        section->oui = p;
+                        p += LLDP_OUI_LEN;
+                        section->subtype = *p++;
+
+                        section->length -= LLDP_OUI_LEN + 1;
+                        l += LLDP_OUI_LEN + 1;
+                }
+
                 section->data = p;
 
                 LIST_FIND_TAIL(section, m->sections, tail);
@@ -294,6 +324,7 @@ int lldp_tlv_packet_enter_container(tlv_packet *m, uint16_t type) {
         tlv_section *s;
 
         assert_return(m, -EINVAL);
+        assert_return(type != LLDP_TYPE_PRIVATE, -EINVAL);
 
         LIST_FOREACH(section, s, m->sections)
                 if (s->type == type)
@@ -305,7 +336,35 @@ int lldp_tlv_packet_enter_container(tlv_packet *m, uint16_t type) {
 
         m->container->read_pos = s->data;
         if (!m->container->read_pos) {
-                m->container = 0;
+                m->container = NULL;
+                return -1;
+        }
+
+        return 0;
+}
+
+int lldp_tlv_packet_enter_container_oui(tlv_packet *m, const uint8_t *oui, uint8_t subtype) {
+        tlv_section *s;
+
+        assert_return(m, -EINVAL);
+        assert_return(oui, -EINVAL);
+
+        LIST_FOREACH(section, s, m->sections) {
+                if (s->type == LLDP_TYPE_PRIVATE &&
+                    s->oui &&
+                    s->subtype == subtype &&
+                    !memcmp(s->oui, oui, LLDP_OUI_LEN))
+                        break;
+        }
+
+        if (!s)
+                return -1;
+
+        m->container = s;
+
+        m->container->read_pos = s->data;
+        if (!m->container->read_pos) {
+                m->container = NULL;
                 return -1;
         }
 
@@ -319,3 +378,270 @@ int lldp_tlv_packet_exit_container(tlv_packet *m) {
 
         return 0;
 }
+
+static int lldp_tlv_packet_read_u16_tlv(tlv_packet *tlv, uint16_t type, uint16_t *value) {
+        int r, r2;
+
+        assert_return(tlv, -EINVAL);
+
+        r = lldp_tlv_packet_enter_container(tlv, type);
+        if (r < 0)
+                goto out;
+
+        r = tlv_packet_read_u16(tlv, value);
+        r2 = lldp_tlv_packet_exit_container(tlv);
+
+ out:
+        return r < 0 ? r : r2;
+}
+
+static int lldp_tlv_packet_read_string_tlv(tlv_packet *tlv, uint16_t type, char **data, uint16_t *length) {
+        char *s;
+        int r, r2;
+
+        assert_return(tlv, -EINVAL);
+
+        r = lldp_tlv_packet_enter_container(tlv, type);
+        if (r < 0)
+                return r;
+
+        r = tlv_packet_read_string(tlv, &s, length);
+        if (r < 0)
+                goto out;
+
+        *data = (char *) s;
+
+ out:
+        r2 = lldp_tlv_packet_exit_container(tlv);
+
+        return r < 0 ? r : r2;
+}
+
+int sd_lldp_packet_read_chassis_id(tlv_packet *tlv,
+                                   uint8_t *type,
+                                   uint8_t **data,
+                                   uint16_t *length) {
+        uint8_t subtype;
+        int r, r2;
+
+        assert_return(tlv, -EINVAL);
+
+        r = lldp_tlv_packet_enter_container(tlv, LLDP_TYPE_CHASSIS_ID);
+        if (r < 0)
+                goto out2;
+
+        r = tlv_packet_read_u8(tlv, &subtype);
+        if (r < 0)
+                goto out1;
+
+        switch (subtype) {
+        case LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS:
+
+                r = tlv_packet_read_bytes(tlv, data, length);
+                if (r < 0)
+                        goto out1;
+
+                break;
+        default:
+                r = -EOPNOTSUPP;
+                break;
+        }
+
+        *type = subtype;
+
+ out1:
+        r2 = lldp_tlv_packet_exit_container(tlv);
+
+ out2:
+        return r < 0 ? r : r2;
+}
+
+int sd_lldp_packet_read_port_id(tlv_packet *tlv,
+                                uint8_t *type,
+                                uint8_t **data,
+                                uint16_t *length) {
+        uint8_t subtype;
+        char *s;
+        int r, r2;
+
+        assert_return(tlv, -EINVAL);
+
+        r = lldp_tlv_packet_enter_container(tlv, LLDP_TYPE_PORT_ID);
+        if (r < 0)
+                goto out2;
+
+        r = tlv_packet_read_u8(tlv, &subtype);
+        if (r < 0)
+                goto out1;
+
+        switch (subtype) {
+        case LLDP_PORT_SUBTYPE_PORT_COMPONENT:
+        case LLDP_PORT_SUBTYPE_INTERFACE_ALIAS:
+        case LLDP_PORT_SUBTYPE_INTERFACE_NAME:
+        case LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED:
+
+                r = tlv_packet_read_string(tlv, &s, length);
+                if (r < 0)
+                        goto out1;
+
+                *data = (uint8_t *) s;
+
+                break;
+        case LLDP_PORT_SUBTYPE_MAC_ADDRESS:
+
+                r = tlv_packet_read_bytes(tlv, data, length);
+                if (r < 0)
+                        goto out1;
+
+                break;
+        default:
+                r = -EOPNOTSUPP;
+                break;
+        }
+
+        *type = subtype;
+
+ out1:
+        r2 = lldp_tlv_packet_exit_container(tlv);
+
+ out2:
+        return r < 0 ? r : r2;
+}
+
+int sd_lldp_packet_read_ttl(tlv_packet *tlv, uint16_t *ttl) {
+        return lldp_tlv_packet_read_u16_tlv(tlv, LLDP_TYPE_TTL, ttl);
+}
+
+int sd_lldp_packet_read_system_name(tlv_packet *tlv,
+                                    char **data,
+                                    uint16_t *length) {
+        return lldp_tlv_packet_read_string_tlv(tlv, LLDP_TYPE_SYSTEM_NAME, data, length);
+}
+
+int sd_lldp_packet_read_system_description(tlv_packet *tlv,
+                                           char **data,
+                                           uint16_t *length) {
+        return lldp_tlv_packet_read_string_tlv(tlv, LLDP_TYPE_SYSTEM_DESCRIPTION, data, length);
+}
+
+int sd_lldp_packet_read_port_description(tlv_packet *tlv,
+                                         char **data,
+                                         uint16_t *length) {
+        return lldp_tlv_packet_read_string_tlv(tlv, LLDP_TYPE_PORT_DESCRIPTION, data, length);
+}
+
+int sd_lldp_packet_read_system_capability(tlv_packet *tlv, uint16_t *data) {
+        return lldp_tlv_packet_read_u16_tlv(tlv, LLDP_TYPE_SYSTEM_CAPABILITIES, data);
+}
+
+int sd_lldp_packet_read_port_vlan_id(tlv_packet *tlv, uint16_t *id) {
+        int r, r2;
+
+        assert_return(tlv, -EINVAL);
+
+        r = lldp_tlv_packet_enter_container_oui(tlv, LLDP_OUI_802_1, LLDP_OUI_SUBTYPE_802_1_PORT_VLAN_ID);
+        if (r < 0)
+                goto out;
+
+        r = tlv_packet_read_u16(tlv, id);
+        r2 = lldp_tlv_packet_exit_container(tlv);
+
+ out:
+        return r < 0 ? r : r2;
+}
+
+int sd_lldp_packet_read_port_protocol_vlan_id(sd_lldp_packet *tlv, uint8_t *flags, uint16_t *id) {
+        int r, r2;
+
+        assert_return(tlv, -EINVAL);
+
+        r = lldp_tlv_packet_enter_container_oui(tlv, LLDP_OUI_802_1, LLDP_OUI_SUBTYPE_802_1_PORT_PROTOCOL_VLAN_ID);
+        if (r < 0)
+                goto out;
+
+        r = tlv_packet_read_u8(tlv, flags);
+        if (r >= 0)
+                r = tlv_packet_read_u16(tlv, id);
+
+        r2 = lldp_tlv_packet_exit_container(tlv);
+
+ out:
+        return r < 0 ? r : r2;
+}
+
+int sd_lldp_packet_read_vlan_name(tlv_packet *tlv, uint16_t *vlan_id, char **name, uint16_t *length) {
+        int r, r2;
+        uint8_t len = 0;
+
+        assert_return(tlv, -EINVAL);
+
+        r = lldp_tlv_packet_enter_container_oui(tlv, LLDP_OUI_802_1, LLDP_OUI_SUBTYPE_802_1_VLAN_NAME);
+        if (r < 0)
+                goto out;
+
+        r = tlv_packet_read_u16(tlv, vlan_id);
+        if (r >= 0)
+                r = tlv_packet_read_u8(tlv, &len);
+        if (r >= 0)
+                r = tlv_packet_read_string(tlv, name, length);
+
+        if (r >= 0 && len < *length)
+                *length = len;
+
+        r2 = lldp_tlv_packet_exit_container(tlv);
+
+ out:
+        return r < 0 ? r : r2;
+}
+
+int sd_lldp_packet_read_management_vid(tlv_packet *tlv, uint16_t *id) {
+        int r, r2;
+
+        assert_return(tlv, -EINVAL);
+
+        r = lldp_tlv_packet_enter_container_oui(tlv, LLDP_OUI_802_1, LLDP_OUI_SUBTYPE_802_1_MANAGEMENT_VID);
+        if (r < 0)
+                goto out;
+
+        r = tlv_packet_read_u16(tlv, id);
+        r2 = lldp_tlv_packet_exit_container(tlv);
+
+ out:
+        return r < 0 ? r : r2;
+}
+
+int sd_lldp_packet_read_link_aggregation(sd_lldp_packet *tlv, uint8_t *status, uint32_t *id) {
+        int r, r2;
+
+        assert_return(tlv, -EINVAL);
+
+        r = lldp_tlv_packet_enter_container_oui(tlv, LLDP_OUI_802_1, LLDP_OUI_SUBTYPE_802_1_LINK_AGGREGATION);
+        if (r < 0)
+                goto out;
+
+        r = tlv_packet_read_u8(tlv, status);
+        if (r >= 0)
+                r = tlv_packet_read_u32(tlv, id);
+
+        r2 = lldp_tlv_packet_exit_container(tlv);
+
+ out:
+        return r < 0 ? r : r2;
+}
+
+int sd_lldp_packet_get_destination_type(tlv_packet *tlv, int *dest) {
+        assert_return(tlv, -EINVAL);
+        assert_return(dest, -EINVAL);
+
+        /* 802.1AB-2009, Table 7-1 */
+        if (!memcmp(&tlv->mac, LLDP_MAC_NEAREST_BRIDGE, ETH_ALEN))
+                *dest = SD_LLDP_DESTINATION_TYPE_NEAREST_BRIDGE;
+        else if (!memcmp(&tlv->mac, LLDP_MAC_NEAREST_NON_TPMR_BRIDGE, ETH_ALEN))
+                *dest = SD_LLDP_DESTINATION_TYPE_NEAREST_NON_TPMR_BRIDGE;
+        else if (!memcmp(&tlv->mac, LLDP_MAC_NEAREST_CUSTOMER_BRIDGE, ETH_ALEN))
+                *dest = SD_LLDP_DESTINATION_TYPE_NEAREST_CUSTOMER_BRIDGE;
+        else
+                return -EINVAL;
+
+        return 0;
+}
index ce3334e1157e4f007854ff936904ee3f6b82623d..2d2c776be6eca224023687a202e4d2c7c1059a58 100644 (file)
 #include "lldp.h"
 #include "list.h"
 
+#include "sd-lldp.h"
+
 typedef struct tlv_packet tlv_packet;
 typedef struct tlv_section tlv_section;
 
+#define LLDP_OUI_LEN 3
+
 struct tlv_section {
         uint16_t type;
         uint16_t length;
+        uint8_t *oui;
+        uint8_t subtype;
 
         uint8_t *read_pos;
         uint8_t *data;
@@ -41,10 +47,16 @@ struct tlv_section {
         LIST_FIELDS(tlv_section, section);
 };
 
+#define LLDP_MAC_NEAREST_BRIDGE          (uint8_t[]) { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e }
+#define LLDP_MAC_NEAREST_NON_TPMR_BRIDGE (uint8_t[]) { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 }
+#define LLDP_MAC_NEAREST_CUSTOMER_BRIDGE (uint8_t[]) { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 }
+
 int tlv_section_new(tlv_section **ret);
 void tlv_section_free(tlv_section *ret);
 
 struct tlv_packet {
+        unsigned n_ref;
+
         uint16_t type;
         uint16_t length;
         usec_t ts;
@@ -61,10 +73,9 @@ struct tlv_packet {
 };
 
 int tlv_packet_new(tlv_packet **ret);
-void tlv_packet_free(tlv_packet *m);
 
-DEFINE_TRIVIAL_CLEANUP_FUNC(tlv_packet*, tlv_packet_free);
-#define _cleanup_tlv_packet_free_ _cleanup_(tlv_packet_freep)
+DEFINE_TRIVIAL_CLEANUP_FUNC(sd_lldp_packet*, sd_lldp_packet_unref);
+#define _cleanup_lldp_packet_unref_ _cleanup_(sd_lldp_packet_unrefp)
 
 int lldp_tlv_packet_open_container(tlv_packet *m, uint16_t type);
 int lldp_tlv_packet_close_container(tlv_packet *m);
@@ -76,6 +87,7 @@ int tlv_packet_append_u32(tlv_packet *m, uint32_t data);
 int tlv_packet_append_string(tlv_packet *m, char *data, uint16_t size);
 
 int lldp_tlv_packet_enter_container(tlv_packet *m, uint16_t type);
+int lldp_tlv_packet_enter_container_oui(tlv_packet *m, const uint8_t *oui, uint8_t subtype);
 int lldp_tlv_packet_exit_container(tlv_packet *m);
 
 int tlv_packet_read_bytes(tlv_packet *m, uint8_t **data, uint16_t *data_length);
index 5e4b283e261127abba20acd68d251782613438f6..19e5cc5f416a818e301b3ee82d36ae686771c84c 100644 (file)
@@ -113,3 +113,16 @@ typedef enum LLDPMedCapability {
         LLDP_MED_CAPABILITY_MAX,
         LLDP_MED_CAPABILITY_INVALID        = -1,
 } LLDPMedCapability;
+
+#define LLDP_OUI_802_1 (uint8_t[]) { 0x00, 0x80, 0xc2 }
+#define LLDP_OUI_802_3 (uint8_t[]) { 0x00, 0x12, 0x0f }
+
+enum {
+        LLDP_OUI_SUBTYPE_802_1_PORT_VLAN_ID            = 1,
+        LLDP_OUI_SUBTYPE_802_1_PORT_PROTOCOL_VLAN_ID   = 2,
+        LLDP_OUI_SUBTYPE_802_1_VLAN_NAME               = 3,
+        LLDP_OUI_SUBTYPE_802_1_PROTOCOL_IDENTITY       = 4,
+        LLDP_OUI_SUBTYPE_802_1_VID_USAGE_DIGEST        = 5,
+        LLDP_OUI_SUBTYPE_802_1_MANAGEMENT_VID          = 6,
+        LLDP_OUI_SUBTYPE_802_1_LINK_AGGREGATION        = 7,
+};
index 17512884f555cfaa2deaaea0b74be276e75a94c8..7aa405a6559cafdd27d3a0176cf38fad68c0409f 100644 (file)
@@ -199,7 +199,7 @@ int lldp_handle_packet(tlv_packet *tlv, uint16_t length) {
                         goto out;
                 }
 
-                /* skip type and lengh encoding */
+                /* skip type and length encoding */
                 p += 2;
                 q = p;
 
@@ -338,7 +338,7 @@ int lldp_handle_packet(tlv_packet *tlv, uint16_t length) {
                 lldp->statistics.stats_frames_in_errors_total ++;
         }
 
-        tlv_packet_free(tlv);
+        sd_lldp_packet_unref(tlv);
 
         return 0;
 }
@@ -455,7 +455,7 @@ int sd_lldp_save(sd_lldp *lldp, const char *lldp_file) {
                         _cleanup_free_ char *s = NULL;
                         char *k, *t;
 
-                        r = lldp_read_chassis_id(p->packet, &type, &length, &mac);
+                        r = sd_lldp_packet_read_chassis_id(p->packet, &type, &mac, &length);
                         if (r < 0)
                                 continue;
 
@@ -468,7 +468,7 @@ int sd_lldp_save(sd_lldp *lldp, const char *lldp_file) {
                                 goto fail;
                         }
 
-                        r = lldp_read_port_id(p->packet, &type, &length, &port_id);
+                        r = sd_lldp_packet_read_port_id(p->packet, &type, &port_id, &length);
                         if (r < 0)
                                 continue;
 
@@ -513,7 +513,7 @@ int sd_lldp_save(sd_lldp *lldp, const char *lldp_file) {
                         free(s);
                         s = k;
 
-                        r = lldp_read_system_name(p->packet, &length, &k);
+                        r = sd_lldp_packet_read_system_name(p->packet, &k, &length);
                         if (r < 0)
                                 k = strappend(s, "'_NAME=N/A' ");
                         else {
@@ -535,7 +535,7 @@ int sd_lldp_save(sd_lldp *lldp, const char *lldp_file) {
                         free(s);
                         s = k;
 
-                        (void) lldp_read_system_capability(p->packet, &data);
+                        (void) sd_lldp_packet_read_system_capability(p->packet, &data);
 
                         sprintf(buf, "'_CAP=%x'", data);
 
@@ -702,3 +702,35 @@ int sd_lldp_new(int ifindex,
 
         return 0;
 }
+
+int sd_lldp_get_packets(sd_lldp *lldp, sd_lldp_packet ***tlvs) {
+        lldp_neighbour_port *p;
+        lldp_chassis *c;
+        Iterator iter;
+        unsigned count = 0, i;
+
+        assert_return(lldp, -EINVAL);
+        assert_return(tlvs, -EINVAL);
+
+        HASHMAP_FOREACH(c, lldp->neighbour_mib, iter) {
+                LIST_FOREACH(port, p, c->ports)
+                        count++;
+        }
+
+        if (!count) {
+                *tlvs = NULL;
+                return 0;
+        }
+
+        *tlvs = new(sd_lldp_packet *, count);
+        if (!*tlvs)
+                return -ENOMEM;
+
+        i = 0;
+        HASHMAP_FOREACH(c, lldp->neighbour_mib, iter) {
+                LIST_FOREACH(port, p, c->ports)
+                        (*tlvs)[i++] = sd_lldp_packet_ref(p->packet);
+        }
+
+        return count;
+}
index 06545aee59e56cae67c9aaafe77e04a2c1d4d202..e57102a57601a336d93f6e65fea545259cbec7be 100644 (file)
 #include <net/ethernet.h>
 #include <arpa/inet.h>
 
+#include "sd-lldp.h"
+#include "sd-event.h"
+#include "event-util.h"
 #include "macro.h"
 #include "lldp.h"
 #include "lldp-tlv.h"
+#include "lldp-network.h"
 
 #define TEST_LLDP_PORT "em1"
 #define TEST_LLDP_TYPE_SYSTEM_NAME "systemd-lldp"
 #define TEST_LLDP_TYPE_SYSTEM_DESC "systemd-lldp-desc"
 
+static int test_fd[2];
+
 static struct ether_addr mac_addr = {
         .ether_addr_octet = {'A', 'B', 'C', '1', '2', '3'}
 };
 
 static int lldp_build_tlv_packet(tlv_packet **ret) {
-        _cleanup_tlv_packet_free_ tlv_packet *m = NULL;
+        _cleanup_lldp_packet_unref_ tlv_packet *m = NULL;
         const uint8_t lldp_dst[] = LLDP_MULTICAST_ADDR;
         struct ether_header ether = {
                 .ether_type = htons(ETHERTYPE_LLDP),
@@ -202,6 +208,15 @@ static int lldp_parse_ttl_tlv(tlv_packet *m) {
         return 0;
 }
 
+static int lldp_get_destination_type(tlv_packet *m) {
+        int dest;
+
+        assert_se(sd_lldp_packet_get_destination_type(m, &dest) >= 0);
+        assert_se(dest == SD_LLDP_DESTINATION_TYPE_NEAREST_BRIDGE);
+
+        return 0;
+}
+
 static int lldp_parse_tlv_packet(tlv_packet *m, int len) {
         uint8_t subtype;
 
@@ -212,20 +227,241 @@ static int lldp_parse_tlv_packet(tlv_packet *m, int len) {
         assert_se(lldp_parse_ttl_tlv(m) >= 0);
         assert_se(lldp_parse_system_desc_tlv(m) >= 0);
 
+        assert_se(lldp_get_destination_type(m) >= 0);
+
         return 0;
 }
 
-int main(int argc, char *argv[]) {
-        _cleanup_tlv_packet_free_ tlv_packet *tlv = NULL;
+static void test_parser(void) {
+        _cleanup_lldp_packet_unref_ tlv_packet *tlv = NULL;
 
         /* form a packet */
         lldp_build_tlv_packet(&tlv);
-
         /* parse the packet */
         tlv_packet_parse_pdu(tlv, tlv->length);
-
         /* verify */
         lldp_parse_tlv_packet(tlv, tlv->length);
+}
+
+int lldp_network_bind_raw_socket(int ifindex) {
+        if (socketpair(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0, test_fd) < 0)
+                return -errno;
+
+        return test_fd[0];
+}
+
+static int lldp_handler_calls;
+static void lldp_handler (sd_lldp *lldp, int event, void *userdata) {
+        lldp_handler_calls++;
+}
+
+static int start_lldp(sd_lldp **lldp, sd_event *e, sd_lldp_cb_t cb, void *cb_data) {
+        int r;
+
+        r = sd_lldp_new(42, "dummy", &mac_addr, lldp);
+        if (r)
+                return r;
+
+        r = sd_lldp_attach_event(*lldp, e, 0);
+        if (r)
+                return r;
+
+        r = sd_lldp_set_callback(*lldp, cb, cb_data);
+        if (r)
+                return r;
+
+        r = sd_lldp_start(*lldp);
+        if (r)
+                return r;
+
+        return 0;
+}
+
+static int stop_lldp(sd_lldp *lldp) {
+        int r;
+
+        r = sd_lldp_stop(lldp);
+        if (r)
+                return r;
+
+        r = sd_lldp_detach_event(lldp);
+        if (r)
+                return r;
+
+        sd_lldp_free(lldp);
+        safe_close(test_fd[1]);
+
+        return 0;
+}
+
+static void test_receive_basic_packet(sd_event *e) {
+        sd_lldp *lldp;
+        sd_lldp_packet **packets;
+        uint8_t type, *data;
+        uint16_t length, ttl;
+        int dest_type;
+        char *str;
+        uint8_t frame[] = {
+                /* Ethernet header */
+                0x01, 0x80, 0xc2, 0x00, 0x00, 0x03,     /* Destination MAC*/
+                0x01, 0x02, 0x03, 0x04, 0x05, 0x06,     /* Source MAC */
+                0x88, 0xcc,                             /* Ethertype */
+                /* LLDP mandatory TLVs */
+                0x02, 0x07, 0x04, 0x00, 0x01, 0x02,     /* Chassis: MAC, 00:01:02:03:04:05 */
+                0x03, 0x04, 0x05,
+                0x04, 0x04, 0x05, 0x31, 0x2f, 0x33,     /* Port: interface name, "1/3" */
+                0x06, 0x02, 0x00, 0x78,                 /* TTL: 120 seconds*/
+                /* LLDP optional TLVs */
+                0x08, 0x04, 0x50, 0x6f, 0x72, 0x74,     /* Port Description: "Port" */
+                0x0a, 0x03, 0x53, 0x59, 0x53,           /* System Name: "SYS" */
+                0x0c, 0x04, 0x66, 0x6f, 0x6f, 0x00,     /* System Description: "foo" (NULL-terminated) */
+                0x00, 0x00                              /* End Of LLDPDU */
+        };
+
+        lldp_handler_calls = 0;
+        assert_se(start_lldp(&lldp, e, lldp_handler, NULL) == 0);
+
+        assert_se(write(test_fd[1], frame, sizeof(frame)) == sizeof(frame));
+        sd_event_run(e, 0);
+        assert_se(lldp_handler_calls == 1);
+        assert_se(sd_lldp_get_packets(lldp, &packets) == 1);
+
+        assert_se(sd_lldp_packet_read_chassis_id(packets[0], &type, &data, &length) == 0);
+        assert_se(type == LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS);
+        assert_se(length == ETH_ALEN);
+        assert_se(!memcmp(data, "\x00\x01\x02\x03\x04\x05", ETH_ALEN));
+
+        assert_se(sd_lldp_packet_read_port_id(packets[0], &type, &data, &length) == 0);
+        assert_se(type == LLDP_PORT_SUBTYPE_INTERFACE_NAME);
+        assert_se(length == 3);
+        assert_se(strneq((char *) data, "1/3", 3));
+
+        assert_se(sd_lldp_packet_read_port_description(packets[0], &str, &length) == 0);
+        assert_se(length == 4);
+        assert_se(strneq(str, "Port", 4));
+
+        assert_se(sd_lldp_packet_read_system_name(packets[0], &str, &length) == 0);
+        assert_se(length == 3);
+        assert_se(strneq(str, "SYS", 3));
+
+        assert_se(sd_lldp_packet_read_system_description(packets[0], &str, &length) == 0);
+        assert_se(length == 4);         /* This is the real length in the TLV packet */
+        assert_se(strneq(str, "foo", 3));
+
+        assert_se(sd_lldp_packet_read_ttl(packets[0], &ttl) == 0);
+        assert_se(ttl == 120);
+
+        assert_se(sd_lldp_packet_get_destination_type(packets[0], &dest_type) == 0);
+        assert_se(dest_type == SD_LLDP_DESTINATION_TYPE_NEAREST_NON_TPMR_BRIDGE);
+
+        sd_lldp_packet_unref(packets[0]);
+        free(packets);
+
+        assert_se(stop_lldp(lldp) == 0);
+}
+
+static void test_receive_incomplete_packet(sd_event *e) {
+        sd_lldp *lldp;
+        sd_lldp_packet **packets;
+        uint8_t frame[] = {
+                /* Ethernet header */
+                0x01, 0x80, 0xc2, 0x00, 0x00, 0x03,     /* Destination MAC*/
+                0x01, 0x02, 0x03, 0x04, 0x05, 0x06,     /* Source MAC */
+                0x88, 0xcc,                             /* Ethertype */
+                /* LLDP mandatory TLVs */
+                0x02, 0x07, 0x04, 0x00, 0x01, 0x02,     /* Chassis: MAC, 00:01:02:03:04:05 */
+                0x03, 0x04, 0x05,
+                0x04, 0x04, 0x05, 0x31, 0x2f, 0x33,     /* Port: interface name, "1/3" */
+                                                        /* Missing TTL */
+                0x00, 0x00                              /* End Of LLDPDU */
+        };
+
+        lldp_handler_calls = 0;
+        assert_se(start_lldp(&lldp, e, lldp_handler, NULL) == 0);
+
+        assert_se(write(test_fd[1], frame, sizeof(frame)) == sizeof(frame));
+        sd_event_run(e, 0);
+        assert_se(lldp_handler_calls == 0);
+        assert_se(sd_lldp_get_packets(lldp, &packets) == 0);
+
+        assert_se(stop_lldp(lldp) == 0);
+}
+
+static void test_receive_oui_packet(sd_event *e) {
+        sd_lldp *lldp;
+        sd_lldp_packet **packets;
+        uint32_t id32;
+        uint16_t id16, len;
+        uint8_t flags;
+        char *str;
+        uint8_t frame[] = {
+                /* Ethernet header */
+                0x01, 0x80, 0xc2, 0x00, 0x00, 0x03,     /* Destination MAC*/
+                0x01, 0x02, 0x03, 0x04, 0x05, 0x06,     /* Source MAC */
+                0x88, 0xcc,                             /* Ethertype */
+                /* LLDP mandatory TLVs */
+                0x02, 0x07, 0x04, 0x00, 0x01, 0x02,     /* Chassis: MAC, 00:01:02:03:04:05 */
+                0x03, 0x04, 0x05,
+                0x04, 0x04, 0x05, 0x31, 0x2f, 0x33,     /* Port TLV: interface name, "1/3" */
+                0x06, 0x02, 0x00, 0x78,                 /* TTL: 120 seconds*/
+                /* LLDP optional TLVs */
+                0xfe, 0x06, 0x00, 0x80, 0xc2, 0x01,     /* Port VLAN ID: 0x1234 */
+                0x12, 0x34,
+                0xfe, 0x07, 0x00, 0x80, 0xc2, 0x02,     /* Port and protocol: flag 1, PPVID 0x7788 */
+                0x01, 0x77, 0x88,
+                0xfe, 0x0d, 0x00, 0x80, 0xc2, 0x03,     /* VLAN Name: ID 0x1234, name "Vlan51" */
+                0x12, 0x34, 0x06, 0x56, 0x6c, 0x61,
+                0x6e, 0x35, 0x31,
+                0xfe, 0x06, 0x00, 0x80, 0xc2, 0x06,     /* Management VID: 0x0102 */
+                0x01, 0x02,
+                0xfe, 0x09, 0x00, 0x80, 0xc2, 0x07,     /* Link aggregation: status 1, ID 0x00140012 */
+                0x01, 0x00, 0x14, 0x00, 0x12,
+                0x00, 0x00                              /* End of LLDPDU */
+        };
+
+        lldp_handler_calls = 0;
+        assert_se(start_lldp(&lldp, e, lldp_handler, NULL) == 0);
+
+        assert_se(write(test_fd[1], frame, sizeof(frame)) == sizeof(frame));
+        sd_event_run(e, 0);
+        assert_se(lldp_handler_calls == 1);
+        assert_se(sd_lldp_get_packets(lldp, &packets) == 1);
+
+        assert_se(sd_lldp_packet_read_port_vlan_id(packets[0], &id16) == 0);
+        assert_se(id16 == 0x1234);
+
+        assert_se(sd_lldp_packet_read_port_protocol_vlan_id(packets[0], &flags, &id16) == 0);
+        assert_se(flags == 1);
+        assert_se(id16 == 0x7788);
+
+        assert_se(sd_lldp_packet_read_vlan_name(packets[0], &id16, &str, &len) == 0);
+        assert_se(id16 == 0x1234);
+        assert_se(len == 6);
+        assert_se(strneq(str, "Vlan51", 6));
+
+        assert_se(sd_lldp_packet_read_management_vid(packets[0], &id16) == 0);
+        assert_se(id16 == 0x0102);
+
+        assert_se(sd_lldp_packet_read_link_aggregation(packets[0], &flags, &id32) == 0);
+        assert_se(flags == 1);
+        assert_se(id32 == 0x00140012);
+
+        sd_lldp_packet_unref(packets[0]);
+        free(packets);
+
+        assert_se(stop_lldp(lldp) == 0);
+}
+
+int main(int argc, char *argv[]) {
+        _cleanup_event_unref_ sd_event *e = NULL;
+
+        test_parser();
+
+        /* LLDP reception tests */
+        assert_se(sd_event_new(&e) == 0);
+        test_receive_basic_packet(e);
+        test_receive_incomplete_packet(e);
+        test_receive_oui_packet(e);
 
         return 0;
 }
index 0d8e37b856c1e9f2e10d4e26494412c1e4d39dda..cf693de5fbf3cb68779df2652fcb5fbe9148d6b5 100644 (file)
@@ -149,6 +149,15 @@ int sd_netlink_message_get_type(sd_netlink_message *m, uint16_t *type) {
         return 0;
 }
 
+int sd_netlink_message_set_flags(sd_netlink_message *m, uint16_t flags) {
+        assert_return(m, -EINVAL);
+        assert_return(flags, -EINVAL);
+
+        m->hdr->nlmsg_flags = flags;
+
+        return 0;
+}
+
 int sd_netlink_message_is_broadcast(sd_netlink_message *m) {
         assert_return(m, -EINVAL);
 
index 21283291919fbbb5f3cefb6d6fba00fb3f084a1f..4a5340e659248dac68e85f595aa7c4cccb98f701 100644 (file)
@@ -97,7 +97,7 @@ static const NLType rtnl_link_info_data_macvlan_types[IFLA_MACVLAN_MAX + 1] = {
         [IFLA_MACVLAN_FLAGS] = { .type = NETLINK_TYPE_U16 },
 };
 
-static const NLType rtnl_link_info_data_bridge_types[IFLA_BRIDGE_MAX + 1] = {
+static const NLType rtnl_link_bridge_management_types[IFLA_BRIDGE_MAX + 1] = {
         [IFLA_BRIDGE_FLAGS]     = { .type = NETLINK_TYPE_U16 },
         [IFLA_BRIDGE_MODE]      = { .type = NETLINK_TYPE_U16 },
 /*
@@ -106,6 +106,15 @@ static const NLType rtnl_link_info_data_bridge_types[IFLA_BRIDGE_MAX + 1] = {
 */
 };
 
+static const NLType rtnl_link_info_data_bridge_types[IFLA_BR_MAX + 1] = {
+        [IFLA_BR_FORWARD_DELAY]  = { .type = NETLINK_TYPE_U32 },
+        [IFLA_BR_HELLO_TIME]     = { .type = NETLINK_TYPE_U32 },
+        [IFLA_BR_MAX_AGE]        = { .type = NETLINK_TYPE_U32 },
+        [IFLA_BR_AGEING_TIME]    = { .type = NETLINK_TYPE_U32 },
+        [IFLA_BR_STP_STATE]      = { .type = NETLINK_TYPE_U32 },
+        [IFLA_BR_PRIORITY]       = { .type = NETLINK_TYPE_U16 },
+};
+
 static const NLType rtnl_link_info_data_vlan_types[IFLA_VLAN_MAX + 1] = {
         [IFLA_VLAN_ID]          = { .type = NETLINK_TYPE_U16 },
 /*
index fd6af7e99b7f39a854bcfbd58a25f94add67270c..2eeb86a683b0ea0d787c3cfa9451a67a008e71b2 100644 (file)
     along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
+#include <net/if.h>
 
 #include "networkd-netdev-bridge.h"
 #include "missing.h"
+#include "netlink-util.h"
+
+/* callback for brige netdev's parameter set */
+static int netdev_bridge_set_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
+        _cleanup_netdev_unref_ NetDev *netdev = userdata;
+        int r;
+
+        assert(netdev);
+        assert(m);
+
+        r = sd_netlink_message_get_errno(m);
+        if (r < 0) {
+                log_netdev_warning_errno(netdev, r, "Bridge parameters could not be set: %m");
+                return 1;
+        }
+
+        log_netdev_debug(netdev, "Bridge parametres set success");
+
+        return 1;
+}
+
+static int netdev_bridge_post_create(NetDev *netdev, Link *link, sd_netlink_message *m) {
+        _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL;
+        Bridge *b;
+        int r;
+
+        assert(netdev);
+
+        b = BRIDGE(netdev);
+
+        assert(b);
+
+        r = sd_rtnl_message_new_link(netdev->manager->rtnl, &req, RTM_NEWLINK, netdev->ifindex);
+        if (r < 0)
+                return log_netdev_error_errno(netdev, r, "Could not allocate RTM_SETLINK message: %m");
+
+        r = sd_netlink_message_set_flags(req, NLM_F_REQUEST | NLM_F_ACK);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not set netlink flags: %m");
+
+        r = sd_netlink_message_open_container(req, IFLA_LINKINFO);
+        if (r < 0)
+                return log_netdev_error_errno(netdev, r, "Could not append IFLA_PROTINFO attribute: %m");
+
+        r = sd_netlink_message_open_container_union(req, IFLA_INFO_DATA, netdev_kind_to_string(netdev->kind));
+        if (r < 0)
+                return log_netdev_error_errno(netdev, r, "Could not append IFLA_INFO_DATA attribute: %m");
+
+        if (b->forward_delay > 0) {
+                r = sd_netlink_message_append_u32(req, IFLA_BR_FORWARD_DELAY, b->forward_delay / USEC_PER_SEC);
+                if (r < 0)
+                        return log_netdev_error_errno(netdev, r, "Could not append IFLA_BR_FORWARD_DELAY attribute: %m");
+        }
+
+        if (b->hello_time > 0) {
+                r = sd_netlink_message_append_u32(req, IFLA_BR_HELLO_TIME, b->hello_time / USEC_PER_SEC );
+                if (r < 0)
+                        return log_netdev_error_errno(netdev, r, "Could not append IFLA_BR_HELLO_TIME attribute: %m");
+        }
+
+        if (b->max_age > 0) {
+                r = sd_netlink_message_append_u32(req, IFLA_BR_MAX_AGE, b->max_age / USEC_PER_SEC);
+                if (r < 0)
+                        return log_netdev_error_errno(netdev, r, "Could not append IFLA_BR_MAX_AGE attribute: %m");
+        }
+
+        r = sd_netlink_message_close_container(req);
+        if (r < 0)
+                return log_netdev_error_errno(netdev, r, "Could not append IFLA_LINKINFO attribute: %m");
+
+        r = sd_netlink_message_close_container(req);
+        if (r < 0)
+                return log_netdev_error_errno(netdev, r, "Could not append IFLA_INFO_DATA attribute: %m");
+
+        r = sd_netlink_call_async(netdev->manager->rtnl, req, netdev_bridge_set_handler, netdev, 0, NULL);
+        if (r < 0)
+                return log_netdev_error_errno(netdev, r, "Could not send rtnetlink message: %m");
+
+        netdev_ref(netdev);
+
+        return r;
+}
 
 const NetDevVTable bridge_vtable = {
         .object_size = sizeof(Bridge),
-        .sections = "Match\0NetDev\0",
+        .sections = "Match\0NetDev\0Bridge\0",
+        .post_create = netdev_bridge_post_create,
         .create_type = NETDEV_CREATE_MASTER,
 };
index a7d02b1c91c61b24fb37f5c9b6dcfadbce6d370b..d3bd15e0d6d128db13b1198dd69ac39c02239902 100644 (file)
@@ -27,6 +27,10 @@ typedef struct Bridge Bridge;
 
 struct Bridge {
         NetDev meta;
+
+        usec_t forward_delay;
+        usec_t hello_time;
+        usec_t max_age;
 };
 
 extern const NetDevVTable bridge_vtable;
index e0bd0e024aa3d1ce53c3cc2c24ea3749e483d8c5..4aac2398503b4962a41c25a19f229fc3aa62a49f 100644 (file)
@@ -86,3 +86,6 @@ Bond.UpDelaySec,             config_parse_sec,                   0,
 Bond.DownDelaySec,           config_parse_sec,                   0,                             offsetof(Bond, downdelay)
 Bond.ARPIntervalSec,         config_parse_sec,                   0,                             offsetof(Bond, arp_interval)
 Bond.LearnPacketIntervalSec, config_parse_sec,                   0,                             offsetof(Bond, lp_interval)
+Bridge.HelloTimeSec,         config_parse_sec,                   0,                             offsetof(Bridge, hello_time)
+Bridge.MaxAgeSec,            config_parse_sec,                   0,                             offsetof(Bridge, max_age)
+Bridge.ForwardDelaySec,      config_parse_sec,                   0,                             offsetof(Bridge, forward_delay)
index ff1edf2c39c575bfc857e9be085517d89d87ec09..3d4865a780b98f435c33125ff98277a55e39fc8f 100644 (file)
@@ -245,6 +245,9 @@ static int netdev_enter_ready(NetDev *netdev) {
                 free(callback);
         }
 
+        if (NETDEV_VTABLE(netdev)->post_create)
+                NETDEV_VTABLE(netdev)->post_create(netdev, NULL, NULL);
+
         return 0;
 }
 
index 1f8510c4f717d56c335b85cb8c7c2bc691c9cae5..3b9ab27b675186ef759aa7551cac78ff70249643 100644 (file)
@@ -141,6 +141,9 @@ struct NetDevVTable {
         /* create netdev, if not done via rtnl */
         int (*create)(NetDev *netdev);
 
+        /* perform additional configuration after netdev has been createad */
+        int (*post_create)(NetDev *netdev, Link *link, sd_netlink_message *message);
+
         /* verify that compulsory configuration options were specified */
         int (*config_verify)(NetDev *netdev, const char *filename);
 };
index 8257ab45da7279d0e904e38d43e6df692a782163..b6f70e191dee7056f0d475f1ada4a7928c587160 100644 (file)
@@ -61,6 +61,7 @@ Route.Destination,                      config_parse_destination,
 Route.Source,                           config_parse_destination,                       0,                             0
 Route.Metric,                           config_parse_route_priority,                    0,                             0
 Route.Scope,                            config_parse_route_scope,                       0,                             0
+Route.PreferredSource,                  config_parse_preferred_src,                     0,                             0
 DHCP.ClientIdentifier,                  config_parse_dhcp_client_identifier,            0,                             offsetof(Network, dhcp_client_identifier)
 DHCP.UseDNS,                            config_parse_bool,                              0,                             offsetof(Network, dhcp_dns)
 DHCP.UseNTP,                            config_parse_bool,                              0,                             offsetof(Network, dhcp_ntp)
index 1f09d95674f3fd38b79ef0b9b5844b9615468186..ee1ddd81fe84c6983986e2a1ed94a9dce688199f 100644 (file)
@@ -305,6 +305,46 @@ int config_parse_gateway(const char *unit,
         return 0;
 }
 
+int config_parse_preferred_src(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) {
+
+        Network *network = userdata;
+        _cleanup_route_free_ Route *n = NULL;
+        union in_addr_union buffer;
+        int r, f;
+
+        assert(filename);
+        assert(section);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        r = route_new_static(network, section_line, &n);
+        if (r < 0)
+                return r;
+
+        r = in_addr_from_string_auto(rvalue, &f, &buffer);
+        if (r < 0) {
+                log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+                           "Preferred source is invalid, ignoring assignment: %s", rvalue);
+                return 0;
+        }
+
+        n->family = f;
+        n->prefsrc_addr = buffer;
+        n = NULL;
+
+        return 0;
+}
+
 int config_parse_destination(const char *unit,
                 const char *filename,
                 unsigned line,
index d090b9c91ec1aa5d775f8de70c971eecfd732f14..11e94d44fbf1fcf69ad8cb48d23bc7ab91532d4a 100644 (file)
@@ -55,6 +55,7 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(Route*, route_free);
 #define _cleanup_route_free_ _cleanup_(route_freep)
 
 int config_parse_gateway(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_preferred_src(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_destination(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_route_priority(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_route_scope(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);
index 0680e526b0982f00779e9c31471878925113cfae..308d42c6e92ac4f40b6a82a13cf87dc5e3f3b0ce 100644 (file)
@@ -28,7 +28,14 @@ enum {
         SD_LLDP_EVENT_UPDATE_INFO       = 0,
 };
 
+enum {
+        SD_LLDP_DESTINATION_TYPE_NEAREST_BRIDGE,
+        SD_LLDP_DESTINATION_TYPE_NEAREST_NON_TPMR_BRIDGE,
+        SD_LLDP_DESTINATION_TYPE_NEAREST_CUSTOMER_BRIDGE,
+};
+
 typedef struct sd_lldp sd_lldp;
+typedef struct tlv_packet sd_lldp_packet;
 
 typedef void (*sd_lldp_cb_t)(sd_lldp *lldp, int event, void *userdata);
 
@@ -43,3 +50,25 @@ int sd_lldp_detach_event(sd_lldp *lldp);
 
 int sd_lldp_set_callback(sd_lldp *lldp, sd_lldp_cb_t cb, void *userdata);
 int sd_lldp_save(sd_lldp *lldp, const char *file);
+
+int sd_lldp_packet_read_chassis_id(sd_lldp_packet *tlv, uint8_t *type, uint8_t **data, uint16_t *length);
+int sd_lldp_packet_read_port_id(sd_lldp_packet *tlv, uint8_t *type, uint8_t **data, uint16_t *length);
+int sd_lldp_packet_read_ttl(sd_lldp_packet *tlv, uint16_t *ttl);
+int sd_lldp_packet_read_system_name(sd_lldp_packet *tlv, char **data, uint16_t *length);
+int sd_lldp_packet_read_system_description(sd_lldp_packet *tlv, char **data, uint16_t *length);
+int sd_lldp_packet_read_system_capability(sd_lldp_packet *tlv, uint16_t *data);
+int sd_lldp_packet_read_port_description(sd_lldp_packet *tlv, char **data, uint16_t *length);
+
+/* IEEE 802.1 organizationally specific TLVs */
+int sd_lldp_packet_read_port_vlan_id(sd_lldp_packet *tlv, uint16_t *id);
+int sd_lldp_packet_read_port_protocol_vlan_id(sd_lldp_packet *tlv, uint8_t *flags, uint16_t *id);
+int sd_lldp_packet_read_vlan_name(sd_lldp_packet *tlv, uint16_t *vlan_id, char **name, uint16_t *length);
+int sd_lldp_packet_read_management_vid(sd_lldp_packet *tlv, uint16_t *id);
+int sd_lldp_packet_read_link_aggregation(sd_lldp_packet *tlv, uint8_t *status, uint32_t *id);
+
+sd_lldp_packet *sd_lldp_packet_ref(sd_lldp_packet *tlv);
+sd_lldp_packet *sd_lldp_packet_unref(sd_lldp_packet *tlv);
+
+int sd_lldp_packet_get_destination_type(sd_lldp_packet *tlv, int *dest);
+
+int sd_lldp_get_packets(sd_lldp *lldp, sd_lldp_packet ***tlvs);
index cb462bf48f0432f0a4ea08a03add9bbcf1418dac..e09b8c8e2d51d1aa65e76958f37b827f2801c9fc 100644 (file)
@@ -104,6 +104,7 @@ int sd_netlink_message_request_dump(sd_netlink_message *m, int dump);
 int sd_netlink_message_is_error(sd_netlink_message *m);
 int sd_netlink_message_get_errno(sd_netlink_message *m);
 int sd_netlink_message_get_type(sd_netlink_message *m, uint16_t *type);
+int sd_netlink_message_set_flags(sd_netlink_message *m, uint16_t flags);
 int sd_netlink_message_is_broadcast(sd_netlink_message *m);
 
 /* rtnl */
index 7de1535fb6e8e165cb403a07f808fed7caf0288b..503e84080349f4f66c253e66699ee9b64cb133ed 100644 (file)
@@ -26,7 +26,9 @@
 #include <math.h>
 #include <signal.h>
 #include <string.h>
+#include <sys/types.h>
 #include <sys/wait.h>
+#include <sys/xattr.h>
 #include <unistd.h>
 
 #include "conf-parser.h"
@@ -2263,6 +2265,38 @@ static void test_strcmp_ptr(void) {
         assert_se(strcmp_ptr("", "") == 0);
 }
 
+static void test_fgetxattrat_fake(void) {
+        char t[] = "/var/tmp/xattrtestXXXXXX";
+        _cleanup_close_ int fd = -1;
+        const char *x;
+        char v[3] = {};
+        int r;
+
+        assert_se(mkdtemp(t));
+        x = strjoina(t, "/test");
+        assert_se(touch(x) >= 0);
+
+        r = setxattr(x, "user.foo", "bar", 3, 0);
+        if (r < 0 && errno == EOPNOTSUPP) /* no xattrs supported on /var/tmp... */
+                goto cleanup;
+        assert_se(r >= 0);
+
+        fd = open(t, O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOCTTY);
+        assert_se(fd >= 0);
+
+        assert_se(fgetxattrat_fake(fd, "test", "user.foo", v, 3, 0) >= 0);
+        assert_se(memcmp(v, "bar", 3) == 0);
+
+        safe_close(fd);
+        fd = open("/", O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOCTTY);
+        assert_se(fd >= 0);
+        assert_se(fgetxattrat_fake(fd, "usr", "user.idontexist", v, 3, 0) == -ENODATA);
+
+cleanup:
+        assert_se(unlink(x) >= 0);
+        assert_se(rmdir(t) >= 0);
+}
+
 int main(int argc, char *argv[]) {
         log_parse_environment();
         log_open();
@@ -2353,6 +2387,7 @@ int main(int argc, char *argv[]) {
         test_parse_mode();
         test_tempfn();
         test_strcmp_ptr();
+        test_fgetxattrat_fake();
 
         return 0;
 }
index 6b86e0a7f7758772ef5e1488b11090ece51a0c45..03349931d9d81e5d05768b2708084b778f1c1888 100644 (file)
@@ -35,5 +35,10 @@ DeviceAllow=/dev/net/tun rwm
 DeviceAllow=/dev/pts/ptmx rw
 DeviceAllow=char-pts rw
 
+# nspawn itself needs access to /dev/loop-control and /dev/loop, to
+# implement the --image= option. Add these here, too.
+DeviceAllow=/dev/loop-control rw
+DeviceAllow=block-loop rw
+
 [Install]
 WantedBy=machines.target