summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/basic/missing.h13
-rw-r--r--src/basic/util.c7
-rw-r--r--src/core/service.c2
-rw-r--r--src/journal-remote/journal-remote.c2
-rw-r--r--src/journal/journal-file.c93
-rw-r--r--src/journal/journal-file.h15
-rw-r--r--src/journal/journal-vacuum.c124
-rw-r--r--src/journal/journal-vacuum.h6
-rw-r--r--src/journal/journalctl.c24
-rw-r--r--src/journal/journald-gperf.gperf2
-rw-r--r--src/journal/journald-server.c353
-rw-r--r--src/journal/journald-server.h9
-rw-r--r--src/journal/journald.c8
-rw-r--r--src/journal/journald.conf2
-rw-r--r--src/journal/test-journal-interleaving.c4
-rw-r--r--src/journal/test-journal.c4
-rw-r--r--src/libsystemd-network/lldp-internal.c251
-rw-r--r--src/libsystemd-network/lldp-internal.h10
-rw-r--r--src/libsystemd-network/lldp-network.c27
-rw-r--r--src/libsystemd-network/lldp-network.h1
-rw-r--r--src/libsystemd-network/lldp-port.c1
-rw-r--r--src/libsystemd-network/lldp-tlv.c342
-rw-r--r--src/libsystemd-network/lldp-tlv.h18
-rw-r--r--src/libsystemd-network/lldp.h13
-rw-r--r--src/libsystemd-network/sd-lldp.c44
-rw-r--r--src/libsystemd-network/test-lldp.c246
-rw-r--r--src/libsystemd/sd-netlink/netlink-message.c9
-rw-r--r--src/libsystemd/sd-netlink/netlink-types.c11
-rw-r--r--src/network/networkd-netdev-bridge.c86
-rw-r--r--src/network/networkd-netdev-bridge.h4
-rw-r--r--src/network/networkd-netdev-gperf.gperf3
-rw-r--r--src/network/networkd-netdev.c3
-rw-r--r--src/network/networkd-netdev.h3
-rw-r--r--src/network/networkd-network-gperf.gperf1
-rw-r--r--src/network/networkd-route.c40
-rw-r--r--src/network/networkd-route.h1
-rw-r--r--src/systemd/sd-lldp.h29
-rw-r--r--src/systemd/sd-netlink.h1
-rw-r--r--src/test/test-util.c35
39 files changed, 1291 insertions, 556 deletions
diff --git a/src/basic/missing.h b/src/basic/missing.h
index 9811b6b23e..1e3af283bb 100644
--- a/src/basic/missing.h
+++ b/src/basic/missing.h
@@ -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
diff --git a/src/basic/util.c b/src/basic/util.c
index c63ec0ceb0..9f520462cf 100644
--- a/src/basic/util.c
+++ b/src/basic/util.c
@@ -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;
diff --git a/src/core/service.c b/src/core/service.c
index cb0394f930..3bb0d913b4 100644
--- a/src/core/service.c
+++ b/src/core/service.c
@@ -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) {
diff --git a/src/journal-remote/journal-remote.c b/src/journal-remote/journal-remote.c
index 5354bf6e51..c920ef7626 100644
--- a/src/journal-remote/journal-remote.c
+++ b/src/journal-remote/journal-remote.c
@@ -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;
}
diff --git a/src/journal/journal-file.c b/src/journal/journal-file.c
index 73d3a4bb9d..1071c6d6d7 100644
--- a/src/journal/journal-file.c
+++ b/src/journal/journal-file.c
@@ -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) {
diff --git a/src/journal/journal-file.h b/src/journal/journal-file.h
index e92b75eabe..f2c07356c8 100644
--- a/src/journal/journal-file.h
+++ b/src/journal/journal-file.h
@@ -36,11 +36,13 @@
#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);
diff --git a/src/journal/journal-vacuum.c b/src/journal/journal-vacuum.c
index 17499bbc30..a394066cb4 100644
--- a/src/journal/journal-vacuum.c
+++ b/src/journal/journal-vacuum.c
@@ -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);
diff --git a/src/journal/journal-vacuum.h b/src/journal/journal-vacuum.h
index c45cc31d0e..49ab90af91 100644
--- a/src/journal/journal-vacuum.h
+++ b/src/journal/journal-vacuum.h
@@ -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);
diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c
index 28ccb80661..d9851db36c 100644
--- a/src/journal/journalctl.c
+++ b/src/journal/journalctl.c
@@ -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;
}
}
diff --git a/src/journal/journald-gperf.gperf b/src/journal/journald-gperf.gperf
index bf7c773009..c154610c54 100644
--- a/src/journal/journald-gperf.gperf
+++ b/src/journal/journald-gperf.gperf
@@ -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)
diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c
index 4566612949..fb172b7f5d 100644
--- a/src/journal/journald-server.c
+++ b/src/journal/journald-server.c
@@ -19,45 +19,44 @@
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
@@ -66,88 +65,61 @@
#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");
diff --git a/src/journal/journald-server.h b/src/journal/journald-server.h
index d954c5190d..535c0ab9ab 100644
--- a/src/journal/journald-server.h
+++ b/src/journal/journald-server.h
@@ -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);
diff --git a/src/journal/journald.c b/src/journal/journald.c
index b2624c6d28..83236ceba9 100644
--- a/src/journal/journald.c
+++ b/src/journal/journald.c
@@ -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;
}
diff --git a/src/journal/journald.conf b/src/journal/journald.conf
index 47eefe91c1..7beb96c671 100644
--- a/src/journal/journald.conf
+++ b/src/journal/journald.conf
@@ -22,9 +22,11 @@
#SystemMaxUse=
#SystemKeepFree=
#SystemMaxFileSize=
+#SystemMaxFiles=100
#RuntimeMaxUse=
#RuntimeKeepFree=
#RuntimeMaxFileSize=
+#RuntimeMaxFiles=100
#MaxRetentionSec=
#MaxFileSec=1month
#ForwardToSyslog=no
diff --git a/src/journal/test-journal-interleaving.c b/src/journal/test-journal-interleaving.c
index adefa1b026..8069339c1f 100644
--- a/src/journal/test-journal-interleaving.c
+++ b/src/journal/test-journal-interleaving.c
@@ -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);
}
diff --git a/src/journal/test-journal.c b/src/journal/test-journal.c
index caaab258c9..01d4bc968a 100644
--- a/src/journal/test-journal.c
+++ b/src/journal/test-journal.c
@@ -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);
}
diff --git a/src/libsystemd-network/lldp-internal.c b/src/libsystemd-network/lldp-internal.c
index 3c04898e92..4012cd483b 100644
--- a/src/libsystemd-network/lldp-internal.c
+++ b/src/libsystemd-network/lldp-internal.c
@@ -21,6 +21,7 @@
***/
#include "lldp-internal.h"
+#include "sd-lldp.h"
/* We store maximum 1K chassis entries */
#define LLDP_MIB_MAX_CHASSIS 1024
@@ -28,207 +29,6 @@
/* 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);
+}
diff --git a/src/libsystemd-network/lldp-internal.h b/src/libsystemd-network/lldp-internal.h
index f4eadbb87e..284cc6720e 100644
--- a/src/libsystemd-network/lldp-internal.h
+++ b/src/libsystemd-network/lldp-internal.h
@@ -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__)
diff --git a/src/libsystemd-network/lldp-network.c b/src/libsystemd-network/lldp-network.c
index 664d2f7867..12a6599ff1 100644
--- a/src/libsystemd-network/lldp-network.c
+++ b/src/libsystemd-network/lldp-network.c
@@ -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);
-}
diff --git a/src/libsystemd-network/lldp-network.h b/src/libsystemd-network/lldp-network.h
index b7f8d3bf80..74ee13a414 100644
--- a/src/libsystemd-network/lldp-network.h
+++ b/src/libsystemd-network/lldp-network.h
@@ -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);
diff --git a/src/libsystemd-network/lldp-port.c b/src/libsystemd-network/lldp-port.c
index 97fe7c1dd3..7486b4c38f 100644
--- a/src/libsystemd-network/lldp-port.c
+++ b/src/libsystemd-network/lldp-port.c
@@ -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;
diff --git a/src/libsystemd-network/lldp-tlv.c b/src/libsystemd-network/lldp-tlv.c
index 0cea5b10a6..66af22e37d 100644
--- a/src/libsystemd-network/lldp-tlv.c
+++ b/src/libsystemd-network/lldp-tlv.c
@@ -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;
+}
diff --git a/src/libsystemd-network/lldp-tlv.h b/src/libsystemd-network/lldp-tlv.h
index ce3334e115..2d2c776be6 100644
--- a/src/libsystemd-network/lldp-tlv.h
+++ b/src/libsystemd-network/lldp-tlv.h
@@ -28,12 +28,18 @@
#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);
diff --git a/src/libsystemd-network/lldp.h b/src/libsystemd-network/lldp.h
index 5e4b283e26..19e5cc5f41 100644
--- a/src/libsystemd-network/lldp.h
+++ b/src/libsystemd-network/lldp.h
@@ -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,
+};
diff --git a/src/libsystemd-network/sd-lldp.c b/src/libsystemd-network/sd-lldp.c
index 17512884f5..7aa405a655 100644
--- a/src/libsystemd-network/sd-lldp.c
+++ b/src/libsystemd-network/sd-lldp.c
@@ -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;
+}
diff --git a/src/libsystemd-network/test-lldp.c b/src/libsystemd-network/test-lldp.c
index 06545aee59..e57102a576 100644
--- a/src/libsystemd-network/test-lldp.c
+++ b/src/libsystemd-network/test-lldp.c
@@ -25,20 +25,26 @@
#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;
}
diff --git a/src/libsystemd/sd-netlink/netlink-message.c b/src/libsystemd/sd-netlink/netlink-message.c
index 0d8e37b856..cf693de5fb 100644
--- a/src/libsystemd/sd-netlink/netlink-message.c
+++ b/src/libsystemd/sd-netlink/netlink-message.c
@@ -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);
diff --git a/src/libsystemd/sd-netlink/netlink-types.c b/src/libsystemd/sd-netlink/netlink-types.c
index 2128329191..4a5340e659 100644
--- a/src/libsystemd/sd-netlink/netlink-types.c
+++ b/src/libsystemd/sd-netlink/netlink-types.c
@@ -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 },
/*
diff --git a/src/network/networkd-netdev-bridge.c b/src/network/networkd-netdev-bridge.c
index fd6af7e99b..2eeb86a683 100644
--- a/src/network/networkd-netdev-bridge.c
+++ b/src/network/networkd-netdev-bridge.c
@@ -20,12 +20,96 @@
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,
};
diff --git a/src/network/networkd-netdev-bridge.h b/src/network/networkd-netdev-bridge.h
index a7d02b1c91..d3bd15e0d6 100644
--- a/src/network/networkd-netdev-bridge.h
+++ b/src/network/networkd-netdev-bridge.h
@@ -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;
diff --git a/src/network/networkd-netdev-gperf.gperf b/src/network/networkd-netdev-gperf.gperf
index e0bd0e024a..4aac239850 100644
--- a/src/network/networkd-netdev-gperf.gperf
+++ b/src/network/networkd-netdev-gperf.gperf
@@ -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)
diff --git a/src/network/networkd-netdev.c b/src/network/networkd-netdev.c
index ff1edf2c39..3d4865a780 100644
--- a/src/network/networkd-netdev.c
+++ b/src/network/networkd-netdev.c
@@ -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;
}
diff --git a/src/network/networkd-netdev.h b/src/network/networkd-netdev.h
index 1f8510c4f7..3b9ab27b67 100644
--- a/src/network/networkd-netdev.h
+++ b/src/network/networkd-netdev.h
@@ -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);
};
diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf
index 8257ab45da..b6f70e191d 100644
--- a/src/network/networkd-network-gperf.gperf
+++ b/src/network/networkd-network-gperf.gperf
@@ -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)
diff --git a/src/network/networkd-route.c b/src/network/networkd-route.c
index 1f09d95674..ee1ddd81fe 100644
--- a/src/network/networkd-route.c
+++ b/src/network/networkd-route.c
@@ -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,
diff --git a/src/network/networkd-route.h b/src/network/networkd-route.h
index d090b9c91e..11e94d44fb 100644
--- a/src/network/networkd-route.h
+++ b/src/network/networkd-route.h
@@ -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);
diff --git a/src/systemd/sd-lldp.h b/src/systemd/sd-lldp.h
index 0680e526b0..308d42c6e9 100644
--- a/src/systemd/sd-lldp.h
+++ b/src/systemd/sd-lldp.h
@@ -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);
diff --git a/src/systemd/sd-netlink.h b/src/systemd/sd-netlink.h
index cb462bf48f..e09b8c8e2d 100644
--- a/src/systemd/sd-netlink.h
+++ b/src/systemd/sd-netlink.h
@@ -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 */
diff --git a/src/test/test-util.c b/src/test/test-util.c
index 7de1535fb6..503e840803 100644
--- a/src/test/test-util.c
+++ b/src/test/test-util.c
@@ -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;
}