* 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.
IFLA_GRE_ENCAP_DPORT,
IFLA_BRIDGE_VLAN_INFO,
IFLA_BRPORT_LEARNING_SYNC,
+ IFLA_BR_PRIORITY,
NDA_IFINDEX,
IFA_FLAGS],
[], [], [[
<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>
<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>
<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>
<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
</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
</example>
<example>
- <title>/etc/systemd/network/vlan1.netdev</title>
+ <title>/etc/systemd/network/25-vlan1.netdev</title>
<programlisting>[Match]
Virtualization=no
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
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
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
</example>
<example>
- <title>/etc/systemd/network/gre.netdev</title>
+ <title>/etc/systemd/network/25-gre.netdev</title>
<programlisting>[NetDev]
Name=gre-tun
Kind=gre
</example>
<example>
- <title>/etc/systemd/network/vti.netdev</title>
+ <title>/etc/systemd/network/25-vti.netdev</title>
<programlisting>[NetDev]
Name=vti-tun
</example>
<example>
- <title>/etc/systemd/network/veth.netdev</title>
+ <title>/etc/systemd/network/25-veth.netdev</title>
<programlisting>[NetDev]
Name=veth-test
Kind=veth
</example>
<example>
- <title>/etc/systemd/network/bond.netdev</title>
+ <title>/etc/systemd/network/25-bond.netdev</title>
<programlisting>[NetDev]
Name=bond1
Kind=bond
</example>
<example>
- <title>/etc/systemd/network/dummy.netdev</title>
+ <title>/etc/systemd/network/25-dummy.netdev</title>
<programlisting>[NetDev]
Name=dummy-test
Kind=dummy
<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>
</example>
<example>
- <title>/etc/systemd/network/bridge-static.network</title>
+ <title>/etc/systemd/network/25-bridge-static.network</title>
<programlisting>[Match]
Name=bridge0
</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
Bridge=bridge0</programlisting>
</example>
<example>
- <title>/etc/systemd/network/ipip.network</title>
+ <title>/etc/systemd/network/25-ipip.network</title>
<programlisting>[Match]
Name=em1
</example>
<example>
- <title>/etc/systemd/network/sit.network</title>
+ <title>/etc/systemd/network/25-sit.network</title>
<programlisting>[Match]
Name=em1
</example>
<example>
- <title>/etc/systemd/network/gre.network</title>
+ <title>/etc/systemd/network/25-gre.network</title>
<programlisting>[Match]
Name=em1
</example>
<example>
- <title>/etc/systemd/network/vti.network</title>
+ <title>/etc/systemd/network/25-vti.network</title>
<programlisting>[Match]
Name=em1
</example>
<example>
- <title>/etc/systemd/network/bond.network</title>
+ <title>/etc/systemd/network/25-bond.network</title>
<programlisting>[Match]
Name=bond1
#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
}
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;
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) {
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) {
}
if (s->active == 0) {
- log_error("Zarro sources specified");
+ log_error("Zero sources specified");
return -EINVAL;
}
#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 */
* 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))
return 0;
}
-void journal_file_close(JournalFile *f) {
+JournalFile* journal_file_close(JournalFile *f) {
assert(f);
#ifdef HAVE_GCRYPT
#endif
free(f);
+ return NULL;
}
static int journal_file_init_header(JournalFile *f, JournalFile *template) {
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;
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)
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 */
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
(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) {
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) {
} 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 */
} 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;
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;
}
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) {
#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 {
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,
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);
char *filename;
uint64_t realtime;
+
sd_id128_t seqnum_id;
uint64_t seqnum;
-
bool have_seqnum;
};
}
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);
* 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;
}
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;
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) {
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;
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) {
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;
}
/* 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) {
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) {
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);
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);
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,
" --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"
ARG_FLUSH,
ARG_ROTATE,
ARG_VACUUM_SIZE,
+ ARG_VACUUM_FILES,
ARG_VACUUM_TIME,
};
{ "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 },
{}
};
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) {
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;
}
}
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)
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;
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
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)
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;
}
}
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
}
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));
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) {
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) {
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);
}
server_rotate(s);
- server_vacuum(s);
+ server_vacuum(s, false, false);
f = find_journal(s, uid);
if (!f)
int rl, r;
_cleanup_free_ char *path = NULL;
+ uint64_t available = 0;
char *c;
assert(s);
}
}
- 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;
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) &&
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");
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) {
* 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");
(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;
}
if (!s->runtime_journal)
return 0;
- system_journal_open(s, true);
+ (void) system_journal_open(s, true);
if (!s->system_journal)
return 0;
}
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.");
}
}
+ 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);
server_flush_to_var(s);
server_sync(s);
- server_vacuum(s);
+ server_vacuum(s, false, false);
touch("/run/systemd/journal/flushed");
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;
}
} 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;
}
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)
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) {
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");
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;
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
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);
#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"
if (r < 0)
goto finish;
- server_vacuum(&server);
+ server_vacuum(&server, false, false);
server_flush_to_var(&server);
server_flush_dev_kmsg(&server);
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;
}
#SystemMaxUse=
#SystemKeepFree=
#SystemMaxFileSize=
+#SystemMaxFiles=100
#RuntimeMaxUse=
#RuntimeKeepFree=
#RuntimeMaxFileSize=
+#RuntimeMaxFiles=100
#MaxRetentionSec=
#MaxFileSec=1month
#ForwardToSyslog=no
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);
}
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);
}
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);
}
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);
}
***/
#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
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;
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);
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;
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;
return 0;
drop:
- tlv_packet_free(tlv);
+ sd_lldp_packet_unref(tlv);
if (new_chassis)
hashmap_remove(neighbour_mib, &c->chassis_id);
if(!p)
return;
- tlv_packet_free(p->packet);
+ sd_lldp_packet_unref(p->packet);
free(p->data);
free(p);
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;
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;
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);
+}
#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;
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__)
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);
-}
#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);
#include "async.h"
#include "lldp-port.h"
#include "lldp-network.h"
+#include "lldp-internal.h"
int lldp_port_start(lldp_port *p) {
int r;
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) {
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;
}
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;
}
p = m->pdu;
- /* extract ethernet herader */
+ /* extract ethernet header */
memcpy(&m->mac, p, ETH_ALEN);
p += sizeof(struct ether_header);
}
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);
tlv_section *s;
assert_return(m, -EINVAL);
+ assert_return(type != LLDP_TYPE_PRIVATE, -EINVAL);
LIST_FOREACH(section, s, m->sections)
if (s->type == 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;
}
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;
+}
#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;
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;
};
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);
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);
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,
+};
goto out;
}
- /* skip type and lengh encoding */
+ /* skip type and length encoding */
p += 2;
q = p;
lldp->statistics.stats_frames_in_errors_total ++;
}
- tlv_packet_free(tlv);
+ sd_lldp_packet_unref(tlv);
return 0;
}
_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;
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;
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 {
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);
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;
+}
#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),
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;
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;
}
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);
[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 },
/*
*/
};
+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 },
/*
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,
};
struct Bridge {
NetDev meta;
+
+ usec_t forward_delay;
+ usec_t hello_time;
+ usec_t max_age;
};
extern const NetDevVTable bridge_vtable;
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)
free(callback);
}
+ if (NETDEV_VTABLE(netdev)->post_create)
+ NETDEV_VTABLE(netdev)->post_create(netdev, NULL, NULL);
+
return 0;
}
/* 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);
};
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)
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,
#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);
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);
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);
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 */
#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"
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();
test_parse_mode();
test_tempfn();
test_strcmp_ptr();
+ test_fgetxattrat_fake();
return 0;
}
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