diff options
Diffstat (limited to 'src')
61 files changed, 1826 insertions, 724 deletions
diff --git a/src/activate/activate.c b/src/activate/activate.c index 2dfc4a0bc4..6a8432314e 100644 --- a/src/activate/activate.c +++ b/src/activate/activate.c @@ -26,7 +26,7 @@ #include <sys/wait.h> #include <unistd.h> -#include "systemd/sd-daemon.h" +#include "sd-daemon.h" #include "log.h" #include "macro.h" @@ -38,6 +38,7 @@ static char** arg_listen = NULL; static bool arg_accept = false; static char** arg_args = NULL; static char** arg_setenv = NULL; +static const char *arg_fdname = NULL; static int add_epoll(int epoll_fd, int fd) { struct epoll_event ev = { @@ -136,8 +137,8 @@ static int launch(char* name, char **argv, char **env, int fds) { length = strv_length(arg_setenv); - /* PATH, TERM, HOME, USER, LISTEN_FDS, LISTEN_PID, NULL */ - envp = new0(char *, length + 7); + /* PATH, TERM, HOME, USER, LISTEN_FDS, LISTEN_PID, LISTEN_FDNAMES, NULL */ + envp = new0(char *, length + 8); if (!envp) return log_oom(); @@ -145,7 +146,9 @@ static int launch(char* name, char **argv, char **env, int fds) { if (strchr(*s, '=')) envp[n_env++] = *s; else { - _cleanup_free_ char *p = strappend(*s, "="); + _cleanup_free_ char *p; + + p = strappend(*s, "="); if (!p) return log_oom(); envp[n_env] = strv_find_prefix(env, p); @@ -164,15 +167,37 @@ static int launch(char* name, char **argv, char **env, int fds) { (asprintf((char**)(envp + n_env++), "LISTEN_PID=%d", getpid()) < 0)) return log_oom(); + if (arg_fdname) { + char *e; + + e = strappend("LISTEN_FDNAMES=", arg_fdname); + if (!e) + return log_oom(); + + for (i = 1; i < (unsigned) fds; i++) { + char *c; + + c = strjoin(e, ":", arg_fdname, NULL); + if (!c) { + free(e); + return log_oom(); + } + + free(e); + e = c; + } + + envp[n_env++] = e; + } + tmp = strv_join(argv, " "); if (!tmp) return log_oom(); log_info("Execing %s (%s)", name, tmp); execvpe(name, argv, envp); - log_error_errno(errno, "Failed to execp %s (%s): %m", name, tmp); - return -errno; + return log_error_errno(errno, "Failed to execp %s (%s): %m", name, tmp); } static int launch1(const char* child, char** argv, char **env, int fd) { @@ -289,6 +314,7 @@ static void help(void) { static int parse_argv(int argc, char *argv[]) { enum { ARG_VERSION = 0x100, + ARG_FDNAME, }; static const struct option options[] = { @@ -297,11 +323,12 @@ static int parse_argv(int argc, char *argv[]) { { "listen", required_argument, NULL, 'l' }, { "accept", no_argument, NULL, 'a' }, { "setenv", required_argument, NULL, 'E' }, - { "environment", required_argument, NULL, 'E' }, /* alias */ + { "environment", required_argument, NULL, 'E' }, /* legacy alias */ + { "fdname", required_argument, NULL, ARG_FDNAME }, {} }; - int c; + int c, r; assert(argc >= 0); assert(argv); @@ -315,25 +342,27 @@ static int parse_argv(int argc, char *argv[]) { case ARG_VERSION: return version(); - case 'l': { - int r = strv_extend(&arg_listen, optarg); + case 'l': + r = strv_extend(&arg_listen, optarg); if (r < 0) - return r; + return log_oom(); break; - } case 'a': arg_accept = true; break; - case 'E': { - int r = strv_extend(&arg_setenv, optarg); + case 'E': + r = strv_extend(&arg_setenv, optarg); if (r < 0) - return r; + return log_oom(); break; - } + + case ARG_FDNAME: + arg_fdname = optarg; + break; case '?': return -EINVAL; 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/strv.c b/src/basic/strv.c index 9524e80a6f..90f0b8c741 100644 --- a/src/basic/strv.c +++ b/src/basic/strv.c @@ -277,8 +277,8 @@ char **strv_split_newlines(const char *s) { } int strv_split_extract(char ***t, const char *s, const char *separators, ExtractFlags flags) { - size_t n = 0, allocated = 0; _cleanup_strv_free_ char **l = NULL; + size_t n = 0, allocated = 0; int r; assert(t); @@ -302,13 +302,16 @@ int strv_split_extract(char ***t, const char *s, const char *separators, Extract l[n] = NULL; } - if (!l) + if (!l) { l = new0(char*, 1); + if (!l) + return -ENOMEM; + } *t = l; l = NULL; - return 0; + return (int) n; } char *strv_join(char **l, const char *separator) { @@ -745,3 +748,41 @@ char **strv_skip(char **l, size_t n) { return l; } + +int strv_extend_n(char ***l, const char *value, size_t n) { + size_t i, j, k; + char **nl; + + assert(l); + + if (!value) + return 0; + if (n == 0) + return 0; + + /* Adds the value value n times to l */ + + k = strv_length(*l); + + nl = realloc(*l, sizeof(char*) * (k + n + 1)); + if (!nl) + return -ENOMEM; + + *l = nl; + + for (i = k; i < k + n; i++) { + nl[i] = strdup(value); + if (!nl[i]) + goto rollback; + } + + nl[i] = NULL; + return 0; + +rollback: + for (j = k; j < i; i++) + free(nl[j]); + + nl[k] = NULL; + return NULL; +} diff --git a/src/basic/strv.h b/src/basic/strv.h index 4c4b6526de..7c1f80230a 100644 --- a/src/basic/strv.h +++ b/src/basic/strv.h @@ -158,3 +158,5 @@ static inline bool strv_fnmatch_or_empty(char* const* patterns, const char *s, i char ***strv_free_free(char ***l); char **strv_skip(char **l, size_t n); + +int strv_extend_n(char ***l, const char *value, size_t n); diff --git a/src/basic/util.c b/src/basic/util.c index c63ec0ceb0..630c7ea9ff 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; @@ -6842,3 +6845,28 @@ int version(void) { SYSTEMD_FEATURES); return 0; } + +bool fdname_is_valid(const char *s) { + const char *p; + + /* Validates a name for $LISTEN_NAMES. We basically allow + * everything ASCII that's not a control character. Also, as + * special exception the ":" character is not allowed, as we + * use that as field separator in $LISTEN_NAMES. + * + * Note that the empty string is explicitly allowed here.*/ + + if (!s) + return false; + + for (p = s; *p; p++) { + if (*p < ' ') + return false; + if (*p >= 127) + return false; + if (*p == ':') + return false; + } + + return p - s < 256; +} diff --git a/src/basic/util.h b/src/basic/util.h index a4e3672130..034410b8a8 100644 --- a/src/basic/util.h +++ b/src/basic/util.h @@ -941,3 +941,5 @@ int receive_one_fd(int transport_fd, int flags); void nop_signal_handler(int sig); int version(void); + +bool fdname_is_valid(const char *s); diff --git a/src/core/dbus-socket.c b/src/core/dbus-socket.c index 4611ad5f86..7444649f8b 100644 --- a/src/core/dbus-socket.c +++ b/src/core/dbus-socket.c @@ -84,6 +84,25 @@ static int property_get_listen( return sd_bus_message_close_container(reply); } + +static int property_get_fdname( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Socket *s = SOCKET(userdata); + + assert(bus); + assert(reply); + assert(s); + + return sd_bus_message_append(reply, "s", socket_fdname(s)); +} + const sd_bus_vtable bus_socket_vtable[] = { SD_BUS_VTABLE_START(0), SD_BUS_PROPERTY("BindIPv6Only", "s", property_get_bind_ipv6_only, offsetof(Socket, bind_ipv6_only), SD_BUS_VTABLE_PROPERTY_CONST), @@ -128,6 +147,7 @@ const sd_bus_vtable bus_socket_vtable[] = { SD_BUS_PROPERTY("Result", "s", property_get_result, offsetof(Socket, result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("NConnections", "u", bus_property_get_unsigned, offsetof(Socket, n_connections), 0), SD_BUS_PROPERTY("NAccepted", "u", bus_property_get_unsigned, offsetof(Socket, n_accepted), 0), + SD_BUS_PROPERTY("FileDescriptorName", "s", property_get_fdname, 0, 0), BUS_EXEC_COMMAND_LIST_VTABLE("ExecStartPre", offsetof(Socket, exec_command[SOCKET_EXEC_START_PRE]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), BUS_EXEC_COMMAND_LIST_VTABLE("ExecStartPost", offsetof(Socket, exec_command[SOCKET_EXEC_START_POST]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), BUS_EXEC_COMMAND_LIST_VTABLE("ExecStopPre", offsetof(Socket, exec_command[SOCKET_EXEC_STOP_PRE]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), diff --git a/src/core/execute.c b/src/core/execute.c index 137a176c18..4664af873f 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -21,18 +21,18 @@ #include <errno.h> #include <fcntl.h> -#include <unistd.h> -#include <string.h> +#include <glob.h> +#include <grp.h> +#include <poll.h> #include <signal.h> -#include <sys/socket.h> -#include <sys/un.h> +#include <string.h> +#include <sys/personality.h> #include <sys/prctl.h> +#include <sys/socket.h> #include <sys/stat.h> -#include <grp.h> -#include <poll.h> -#include <glob.h> +#include <sys/un.h> +#include <unistd.h> #include <utmpx.h> -#include <sys/personality.h> #ifdef HAVE_PAM #include <security/pam_appl.h> @@ -50,37 +50,38 @@ #include <sys/apparmor.h> #endif -#include "barrier.h" #include "sd-messages.h" -#include "rm-rf.h" -#include "strv.h" -#include "macro.h" + +#include "af-list.h" +#include "async.h" +#include "barrier.h" +#include "bus-endpoint.h" +#include "cap-list.h" #include "capability.h" -#include "util.h" -#include "log.h" -#include "ioprio.h" -#include "securebits.h" -#include "namespace.h" -#include "exit-status.h" -#include "missing.h" -#include "utmp-wtmp.h" #include "def.h" -#include "path-util.h" #include "env-util.h" -#include "fileio.h" -#include "unit.h" -#include "async.h" -#include "selinux-util.h" #include "errno-list.h" -#include "af-list.h" -#include "mkdir.h" -#include "smack-util.h" -#include "bus-endpoint.h" -#include "cap-list.h" +#include "exit-status.h" +#include "fileio.h" #include "formats-util.h" +#include "ioprio.h" +#include "log.h" +#include "macro.h" +#include "missing.h" +#include "mkdir.h" +#include "namespace.h" +#include "path-util.h" #include "process-util.h" -#include "terminal-util.h" +#include "rm-rf.h" +#include "securebits.h" +#include "selinux-util.h" #include "signal-util.h" +#include "smack-util.h" +#include "strv.h" +#include "terminal-util.h" +#include "unit.h" +#include "util.h" +#include "utmp-wtmp.h" #ifdef HAVE_APPARMOR #include "apparmor-util.h" @@ -1198,6 +1199,7 @@ static void do_idle_pipe_dance(int idle_pipe[4]) { static int build_environment( const ExecContext *c, unsigned n_fds, + char ** fd_names, usec_t watchdog_usec, const char *home, const char *username, @@ -1211,11 +1213,13 @@ static int build_environment( assert(c); assert(ret); - our_env = new0(char*, 10); + our_env = new0(char*, 11); if (!our_env) return -ENOMEM; if (n_fds > 0) { + _cleanup_free_ char *joined = NULL; + if (asprintf(&x, "LISTEN_PID="PID_FMT, getpid()) < 0) return -ENOMEM; our_env[n_env++] = x; @@ -1223,6 +1227,15 @@ static int build_environment( if (asprintf(&x, "LISTEN_FDS=%u", n_fds) < 0) return -ENOMEM; our_env[n_env++] = x; + + joined = strv_join(fd_names, ":"); + if (!joined) + return -ENOMEM; + + x = strjoin("LISTEN_FDNAMES=", joined, NULL); + if (!x) + return -ENOMEM; + our_env[n_env++] = x; } if (watchdog_usec > 0) { @@ -1273,7 +1286,7 @@ static int build_environment( } our_env[n_env++] = NULL; - assert(n_env <= 10); + assert(n_env <= 11); *ret = our_env; our_env = NULL; @@ -1850,7 +1863,7 @@ static int exec_child( #endif } - r = build_environment(context, n_fds, params->watchdog_usec, home, username, shell, &our_env); + r = build_environment(context, n_fds, params->fd_names, params->watchdog_usec, home, username, shell, &our_env); if (r < 0) { *exit_status = EXIT_MEMORY; return r; diff --git a/src/core/execute.h b/src/core/execute.h index 2c93044748..f1c37116fd 100644 --- a/src/core/execute.h +++ b/src/core/execute.h @@ -208,19 +208,30 @@ struct ExecContext { struct ExecParameters { char **argv; - int *fds; unsigned n_fds; + + int *fds; + char **fd_names; + unsigned n_fds; + char **environment; + bool apply_permissions; bool apply_chroot; bool apply_tty_stdin; + bool confirm_spawn; bool selinux_context_net; + CGroupMask cgroup_supported; const char *cgroup_path; bool cgroup_delegate; + const char *runtime_prefix; + usec_t watchdog_usec; + int *idle_pipe; + char *bus_endpoint_path; int bus_endpoint_fd; }; diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4 index 2333926e7d..89e624b557 100644 --- a/src/core/load-fragment-gperf.gperf.m4 +++ b/src/core/load-fragment-gperf.gperf.m4 @@ -287,6 +287,7 @@ Socket.MessageQueueMaxMessages, config_parse_long, 0, Socket.MessageQueueMessageSize, config_parse_long, 0, offsetof(Socket, mq_msgsize) Socket.RemoveOnStop, config_parse_bool, 0, offsetof(Socket, remove_on_stop) Socket.Symlinks, config_parse_unit_path_strv_printf, 0, offsetof(Socket, symlinks) +Socket.FileDescriptorName, config_parse_fdname, 0, 0 Socket.Service, config_parse_socket_service, 0, 0 m4_ifdef(`HAVE_SMACK', `Socket.SmackLabel, config_parse_string, 0, offsetof(Socket, smack) diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index fc2755cb92..b1d4c6b57d 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -1475,10 +1475,10 @@ int config_parse_socket_service( void *userdata) { _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_free_ char *p = NULL; Socket *s = data; - int r; Unit *x; - _cleanup_free_ char *p = NULL; + int r; assert(filename); assert(lvalue); @@ -1507,6 +1507,50 @@ int config_parse_socket_service( return 0; } +int config_parse_fdname( + 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) { + + _cleanup_free_ char *p = NULL; + Socket *s = data; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + if (isempty(rvalue)) { + s->fdname = mfree(s->fdname); + return 0; + } + + r = unit_name_printf(UNIT(s), rvalue, &p); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %s", rvalue); + return 0; + } + + if (!fdname_is_valid(p)) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid file descriptor name, ignoring: %s", p); + return 0; + } + + free(s->fdname); + s->fdname = p; + p = NULL; + + return 0; +} + int config_parse_service_sockets( const char *unit, const char *filename, diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h index 6ee7c71bc4..8661cbfedc 100644 --- a/src/core/load-fragment.h +++ b/src/core/load-fragment.h @@ -107,6 +107,7 @@ int config_parse_protect_system(const char* unit, const char *filename, unsigned int config_parse_bus_name(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_exec_utmp_mode(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_working_directory(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_fdname(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); /* gperf prototypes */ const struct ConfigPerfItem* load_fragment_gperf_lookup(const char *key, unsigned length); diff --git a/src/core/manager.c b/src/core/manager.c index 9de9691a47..526d4d1cef 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -495,6 +495,7 @@ static void manager_clean_environment(Manager *m) { "MANAGERPID", "LISTEN_PID", "LISTEN_FDS", + "LISTEN_FDNAMES", "WATCHDOG_PID", "WATCHDOG_USEC", NULL); diff --git a/src/core/service.c b/src/core/service.c index cb0394f930..8c339765a4 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -261,6 +261,7 @@ static void service_fd_store_unlink(ServiceFDStore *fs) { sd_event_source_unref(fs->event_source); } + free(fs->fdname); safe_close(fs->fd); free(fs); } @@ -334,7 +335,7 @@ static int on_fd_store_io(sd_event_source *e, int fd, uint32_t revents, void *us return 0; } -static int service_add_fd_store(Service *s, int fd) { +static int service_add_fd_store(Service *s, int fd, const char *name) { ServiceFDStore *fs; int r; @@ -361,9 +362,13 @@ static int service_add_fd_store(Service *s, int fd) { fs->fd = fd; fs->service = s; + fs->fdname = strdup(name ?: "stored"); + if (!fs->fdname) + return -ENOMEM; r = sd_event_add_io(UNIT(s)->manager->event, &fs->event_source, fd, 0, on_fd_store_io, fs); if (r < 0) { + free(fs->fdname); free(fs); return r; } @@ -376,7 +381,7 @@ static int service_add_fd_store(Service *s, int fd) { return 1; } -static int service_add_fd_store_set(Service *s, FDSet *fds) { +static int service_add_fd_store_set(Service *s, FDSet *fds, const char *name) { int r; assert(s); @@ -391,7 +396,7 @@ static int service_add_fd_store_set(Service *s, FDSet *fds) { if (fd < 0) break; - r = service_add_fd_store(s, fd); + r = service_add_fd_store(s, fd, name); if (r < 0) return log_unit_error_errno(UNIT(s), r, "Couldn't add fd to fd store: %m"); if (r > 0) { @@ -884,7 +889,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) { @@ -957,62 +961,79 @@ static int service_coldplug(Unit *u) { return 0; } -static int service_collect_fds(Service *s, int **fds, unsigned *n_fds) { +static int service_collect_fds(Service *s, int **fds, char ***fd_names) { + _cleanup_strv_free_ char **rfd_names = NULL; _cleanup_free_ int *rfds = NULL; - unsigned rn_fds = 0; - Iterator i; - int r; - Unit *u; + int rn_fds = 0, r; assert(s); assert(fds); - assert(n_fds); + assert(fd_names); - if (s->socket_fd >= 0) - return 0; + if (s->socket_fd >= 0) { - SET_FOREACH(u, UNIT(s)->dependencies[UNIT_TRIGGERED_BY], i) { - int *cfds; - unsigned cn_fds; - Socket *sock; + /* Pass the per-connection socket */ - if (u->type != UNIT_SOCKET) - continue; + rfds = new(int, 1); + if (!rfds) + return -ENOMEM; + rfds[0] = s->socket_fd; - sock = SOCKET(u); + rfd_names = strv_new("connection", NULL); + if (!rfd_names) + return -ENOMEM; - r = socket_collect_fds(sock, &cfds, &cn_fds); - if (r < 0) - return r; + rn_fds = 1; + } else { + Iterator i; + Unit *u; - if (cn_fds <= 0) { - free(cfds); - continue; - } + /* Pass all our configured sockets for singleton services */ - if (!rfds) { - rfds = cfds; - rn_fds = cn_fds; - } else { - int *t; + SET_FOREACH(u, UNIT(s)->dependencies[UNIT_TRIGGERED_BY], i) { + _cleanup_free_ int *cfds = NULL; + Socket *sock; + int cn_fds; - t = realloc(rfds, (rn_fds + cn_fds) * sizeof(int)); - if (!t) { - free(cfds); - return -ENOMEM; - } + if (u->type != UNIT_SOCKET) + continue; + + sock = SOCKET(u); + + cn_fds = socket_collect_fds(sock, &cfds); + if (cn_fds < 0) + return cn_fds; + + if (cn_fds <= 0) + continue; + + if (!rfds) { + rfds = cfds; + rn_fds = cn_fds; + + cfds = NULL; + } else { + int *t; + + t = realloc(rfds, (rn_fds + cn_fds) * sizeof(int)); + if (!t) + return -ENOMEM; - memcpy(t + rn_fds, cfds, cn_fds * sizeof(int)); - rfds = t; - rn_fds += cn_fds; + memcpy(t + rn_fds, cfds, cn_fds * sizeof(int)); - free(cfds); + rfds = t; + rn_fds += cn_fds; + } + r = strv_extend_n(&rfd_names, socket_fdname(sock), cn_fds); + if (r < 0) + return r; } } if (s->n_fd_store > 0) { ServiceFDStore *fs; + char **nl; int *t; t = realloc(rfds, (rn_fds + s->n_fd_store) * sizeof(int)); @@ -1020,15 +1041,32 @@ static int service_collect_fds(Service *s, int **fds, unsigned *n_fds) { return -ENOMEM; rfds = t; - LIST_FOREACH(fd_store, fs, s->fd_store) - rfds[rn_fds++] = fs->fd; + + nl = realloc(rfd_names, (rn_fds + s->n_fd_store + 1) * sizeof(char*)); + if (!nl) + return -ENOMEM; + + rfd_names = nl; + + LIST_FOREACH(fd_store, fs, s->fd_store) { + rfds[rn_fds] = fs->fd; + rfd_names[rn_fds] = strdup(strempty(fs->fdname)); + if (!rfd_names[rn_fds]) + return -ENOMEM; + + rn_fds++; + } + + rfd_names[rn_fds] = NULL; } *fds = rfds; - *n_fds = rn_fds; + *fd_names = rfd_names; rfds = NULL; - return 0; + rfd_names = NULL; + + return rn_fds; } static int service_spawn( @@ -1042,15 +1080,13 @@ static int service_spawn( bool is_control, pid_t *_pid) { - pid_t pid; - int r; - int *fds = NULL; - _cleanup_free_ int *fdsbuf = NULL; - unsigned n_fds = 0, n_env = 0; + _cleanup_strv_free_ char **argv = NULL, **final_env = NULL, **our_env = NULL, **fd_names = NULL; _cleanup_free_ char *bus_endpoint_path = NULL; - _cleanup_strv_free_ char - **argv = NULL, **final_env = NULL, **our_env = NULL; + _cleanup_free_ int *fds = NULL; + unsigned n_fds = 0, n_env = 0; const char *path; + pid_t pid; + ExecParameters exec_params = { .apply_permissions = apply_permissions, .apply_chroot = apply_chroot, @@ -1059,6 +1095,8 @@ static int service_spawn( .selinux_context_net = s->socket_fd_selinux_context_net }; + int r; + assert(s); assert(c); assert(_pid); @@ -1078,16 +1116,11 @@ static int service_spawn( s->exec_context.std_output == EXEC_OUTPUT_SOCKET || s->exec_context.std_error == EXEC_OUTPUT_SOCKET) { - if (s->socket_fd >= 0) { - fds = &s->socket_fd; - n_fds = 1; - } else { - r = service_collect_fds(s, &fdsbuf, &n_fds); - if (r < 0) - goto fail; + r = service_collect_fds(s, &fds, &fd_names); + if (r < 0) + goto fail; - fds = fdsbuf; - } + n_fds = r; } if (timeout > 0) { @@ -1125,7 +1158,7 @@ static int service_spawn( goto fail; } - if (UNIT_DEREF(s->accept_socket)) { + if (s->socket_fd >= 0) { union sockaddr_union sa; socklen_t salen = sizeof(sa); @@ -1191,6 +1224,7 @@ static int service_spawn( exec_params.argv = argv; exec_params.fds = fds; + exec_params.fd_names = fd_names; exec_params.n_fds = n_fds; exec_params.environment = final_env; exec_params.confirm_spawn = UNIT(s)->manager->confirm_spawn; @@ -1778,6 +1812,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) { @@ -2053,13 +2088,16 @@ static int service_serialize(Unit *u, FILE *f, FDSet *fds) { } LIST_FOREACH(fd_store, fs, s->fd_store) { + _cleanup_free_ char *c = NULL; int copy; copy = fdset_put_dup(fds, fs->fd); if (copy < 0) return copy; - unit_serialize_item_format(u, f, "fd-store-fd", "%i", copy); + c = cescape(fs->fdname); + + unit_serialize_item_format(u, f, "fd-store-fd", "%i %s", copy, strempty(c)); } if (s->main_exec_status.pid > 0) { @@ -2189,12 +2227,24 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value, s->bus_endpoint_fd = fdset_remove(fds, fd); } } else if (streq(key, "fd-store-fd")) { + const char *fdv; + size_t pf; int fd; - if (safe_atoi(value, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd)) + pf = strcspn(value, WHITESPACE); + fdv = strndupa(value, pf); + + if (safe_atoi(fdv, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd)) log_unit_debug(u, "Failed to parse fd-store-fd value: %s", value); else { - r = service_add_fd_store(s, fd); + _cleanup_free_ char *t = NULL; + const char *fdn; + + fdn = value + pf; + fdn += strspn(fdn, WHITESPACE); + (void) cunescape(fdn, 0, &t); + + r = service_add_fd_store(s, fd, t); if (r < 0) log_unit_error_errno(u, r, "Failed to add fd to store: %m"); else if (r > 0) @@ -2948,8 +2998,17 @@ static void service_notify_message(Unit *u, pid_t pid, char **tags, FDSet *fds) if (strv_find(tags, "WATCHDOG=1")) service_reset_watchdog(s); - if (strv_find(tags, "FDSTORE=1")) - service_add_fd_store_set(s, fds); + if (strv_find(tags, "FDSTORE=1")) { + const char *name; + + name = strv_find_startswith(tags, "FDNAME="); + if (name && !fdname_is_valid(name)) { + log_unit_warning(u, "Passed FDNAME= name is invalid, ignoring."); + name = NULL; + } + + service_add_fd_store_set(s, fds, name); + } /* Notify clients about changed status or main pid */ if (notify_dbus) diff --git a/src/core/service.h b/src/core/service.h index a8d42706bd..61bb44fbcf 100644 --- a/src/core/service.h +++ b/src/core/service.h @@ -97,6 +97,7 @@ struct ServiceFDStore { Service *service; int fd; + char *fdname; sd_event_source *event_source; LIST_FIELDS(ServiceFDStore, fd_store); diff --git a/src/core/socket.c b/src/core/socket.c index 3250e7efc6..6300b20f3e 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -508,6 +508,7 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) { "%sTCPCongestion: %s\n" "%sRemoveOnStop: %s\n" "%sWritable: %s\n" + "%sFDName: %s\n" "%sSELinuxContextFromNet: %s\n", prefix, socket_state_to_string(s->state), prefix, socket_result_to_string(s->result), @@ -525,6 +526,7 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) { prefix, strna(s->tcp_congestion), prefix, yes_no(s->remove_on_stop), prefix, yes_no(s->writable), + prefix, socket_fdname(s), prefix, yes_no(s->selinux_context_from_net)); if (s->control_pid > 0) @@ -1028,8 +1030,6 @@ static int fifo_address_create( fail: mac_selinux_create_file_clear(); - safe_close(fd); - return r; } @@ -2630,49 +2630,43 @@ static int socket_dispatch_timer(sd_event_source *source, usec_t usec, void *use return 0; } -int socket_collect_fds(Socket *s, int **fds, unsigned *n_fds) { - int *rfds; - unsigned rn_fds, k; - int i; +int socket_collect_fds(Socket *s, int **fds) { + int *rfds, k = 0, n = 0; SocketPort *p; assert(s); assert(fds); - assert(n_fds); /* Called from the service code for requesting our fds */ - rn_fds = 0; LIST_FOREACH(port, p, s->ports) { if (p->fd >= 0) - rn_fds++; - rn_fds += p->n_auxiliary_fds; + n++; + n += p->n_auxiliary_fds; } - if (rn_fds <= 0) { + if (n <= 0) { *fds = NULL; - *n_fds = 0; return 0; } - rfds = new(int, rn_fds); + rfds = new(int, n); if (!rfds) return -ENOMEM; - k = 0; LIST_FOREACH(port, p, s->ports) { + int i; + if (p->fd >= 0) rfds[k++] = p->fd; for (i = 0; i < p->n_auxiliary_fds; ++i) rfds[k++] = p->auxiliary_fds[i]; } - assert(k == rn_fds); + assert(k == n); *fds = rfds; - *n_fds = rn_fds; - - return 0; + return n; } static void socket_reset_failed(Unit *u) { @@ -2768,6 +2762,19 @@ static int socket_get_timeout(Unit *u, uint64_t *timeout) { return 1; } +char *socket_fdname(Socket *s) { + assert(s); + + /* Returns the name to use for $LISTEN_NAMES. If the user + * didn't specify anything specifically, use the socket unit's + * name as fallback. */ + + if (s->fdname) + return s->fdname; + + return UNIT(s)->id; +} + static const char* const socket_exec_command_table[_SOCKET_EXEC_COMMAND_MAX] = { [SOCKET_EXEC_START_PRE] = "StartPre", [SOCKET_EXEC_START_CHOWN] = "StartChown", diff --git a/src/core/socket.h b/src/core/socket.h index d20dc8d81a..94cda8a90d 100644 --- a/src/core/socket.h +++ b/src/core/socket.h @@ -154,16 +154,22 @@ struct Socket { char *user, *group; bool reset_cpu_usage:1; + + char *fdname; }; /* Called from the service code when collecting fds */ -int socket_collect_fds(Socket *s, int **fds, unsigned *n_fds); +int socket_collect_fds(Socket *s, int **fds); /* Called from the service code when a per-connection service ended */ void socket_connection_unref(Socket *s); void socket_free_ports(Socket *s); +int socket_instantiate_service(Socket *s); + +char *socket_fdname(Socket *s); + extern const UnitVTable socket_vtable; const char* socket_exec_command_to_string(SocketExecCommand i) _const_; @@ -173,5 +179,3 @@ const char* socket_result_to_string(SocketResult i) _const_; SocketResult socket_result_from_string(const char *s) _pure_; const char* socket_port_type_to_string(SocketPort *p) _pure_; - -int socket_instantiate_service(Socket *s); diff --git a/src/gpt-auto-generator/gpt-auto-generator.c b/src/gpt-auto-generator/gpt-auto-generator.c index dbb6648daa..96425c5b07 100644 --- a/src/gpt-auto-generator/gpt-auto-generator.c +++ b/src/gpt-auto-generator/gpt-auto-generator.c @@ -871,7 +871,6 @@ static int get_block_device_harder(const char *path, dev_t *dev) { goto fallback; found = de; - break; } if (!found) 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 0b7d35cdf1..06949a1e83 100644 --- a/src/libsystemd-network/sd-lldp.c +++ b/src/libsystemd-network/sd-lldp.c @@ -197,7 +197,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; @@ -336,7 +336,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; } @@ -453,7 +453,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; @@ -466,7 +466,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; @@ -511,7 +511,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 { @@ -533,7 +533,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); @@ -700,3 +700,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-daemon/sd-daemon.c b/src/libsystemd/sd-daemon/sd-daemon.c index 5fb0b73d02..437518119b 100644 --- a/src/libsystemd/sd-daemon/sd-daemon.c +++ b/src/libsystemd/sd-daemon/sd-daemon.c @@ -19,25 +19,37 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <sys/stat.h> -#include <sys/socket.h> -#include <sys/un.h> -#include <netinet/in.h> -#include <stdlib.h> #include <errno.h> -#include <unistd.h> -#include <string.h> -#include <stdarg.h> -#include <stdio.h> -#include <stddef.h> #include <limits.h> #include <mqueue.h> +#include <netinet/in.h> +#include <stdarg.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/un.h> +#include <unistd.h> -#include "util.h" #include "path-util.h" #include "socket-util.h" +#include "strv.h" +#include "util.h" + #include "sd-daemon.h" +static void unsetenv_all(bool unset_environment) { + + if (!unset_environment) + return; + + unsetenv("LISTEN_PID"); + unsetenv("LISTEN_FDS"); + unsetenv("LISTEN_FDNAMES"); +} + _public_ int sd_listen_fds(int unset_environment) { const char *e; unsigned n; @@ -79,12 +91,49 @@ _public_ int sd_listen_fds(int unset_environment) { r = (int) n; finish: - if (unset_environment) { - unsetenv("LISTEN_PID"); - unsetenv("LISTEN_FDS"); + unsetenv_all(unset_environment); + return r; +} + +_public_ int sd_listen_fds_with_names(int unset_environment, char ***names) { + _cleanup_strv_free_ char **l = NULL; + bool have_names; + int n_names = 0, n_fds; + const char *e; + int r; + + if (!names) + return sd_listen_fds(unset_environment); + + e = getenv("LISTEN_FDNAMES"); + if (e) { + n_names = strv_split_extract(&l, e, ":", EXTRACT_DONT_COALESCE_SEPARATORS); + if (n_names < 0) { + unsetenv_all(unset_environment); + return n_names; + } + + have_names = true; + } else + have_names = false; + + n_fds = sd_listen_fds(unset_environment); + if (n_fds <= 0) + return n_fds; + + if (have_names) { + if (n_names != n_fds) + return -EINVAL; + } else { + r = strv_extend_n(&l, "unknown", n_fds); + if (r < 0) + return r; } - return r; + *names = l; + l = NULL; + + return n_fds; } _public_ int sd_is_fifo(int fd, const char *path) { diff --git a/src/libsystemd/sd-device/sd-device.c b/src/libsystemd/sd-device/sd-device.c index c528a157c1..e46546ed91 100644 --- a/src/libsystemd/sd-device/sd-device.c +++ b/src/libsystemd/sd-device/sd-device.c @@ -295,15 +295,27 @@ _public_ int sd_device_new_from_subsystem_sysname(sd_device **ret, const char *s } else return -EINVAL; } else { - syspath = strjoina("/sys/subsystem/", subsystem, "/devices/", sysname); + char *name; + size_t len = 0; + + /* translate sysname back to sysfs filename */ + name = strdupa(sysname); + while (name[len] != '\0') { + if (name[len] == '/') + name[len] = '!'; + + len ++; + } + + syspath = strjoina("/sys/subsystem/", subsystem, "/devices/", name); if (access(syspath, F_OK) >= 0) return sd_device_new_from_syspath(ret, syspath); - syspath = strjoina("/sys/bus/", subsystem, "/devices/", sysname); + syspath = strjoina("/sys/bus/", subsystem, "/devices/", name); if (access(syspath, F_OK) >= 0) return sd_device_new_from_syspath(ret, syspath); - syspath = strjoina("/sys/class/", subsystem, "/", sysname); + syspath = strjoina("/sys/class/", subsystem, "/", name); if (access(syspath, F_OK) >= 0) return sd_device_new_from_syspath(ret, syspath); } 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-daemon.h b/src/systemd/sd-daemon.h index 861dc8f1f4..214e77cab1 100644 --- a/src/systemd/sd-daemon.h +++ b/src/systemd/sd-daemon.h @@ -76,6 +76,8 @@ _SD_BEGIN_DECLARATIONS; */ int sd_listen_fds(int unset_environment); +int sd_listen_fds_with_names(int unset_environment, char ***names); + /* Helper call for identifying a passed file descriptor. Returns 1 if the file descriptor is a FIFO in the file system stored under the 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-daemon.c b/src/test/test-daemon.c index 7e0ac754d1..45fb554445 100644 --- a/src/test/test-daemon.c +++ b/src/test/test-daemon.c @@ -21,9 +21,22 @@ #include <unistd.h> -#include "systemd/sd-daemon.h" +#include "sd-daemon.h" + +#include "strv.h" int main(int argc, char*argv[]) { + _cleanup_strv_free_ char **l = NULL; + int n, i; + + n = sd_listen_fds_with_names(false, &l); + if (n < 0) { + log_error_errno(n, "Failed to get listening fds: %m"); + return EXIT_FAILURE; + } + + for (i = 0; i < n; i++) + log_info("fd=%i name=%s\n", SD_LISTEN_FDS_START + i, l[i]); sd_notify(0, "STATUS=Starting up"); @@ -49,5 +62,5 @@ int main(int argc, char*argv[]) { "STOPPING=1"); sleep(5); - return 0; + return EXIT_SUCCESS; } diff --git a/src/test/test-strv.c b/src/test/test-strv.c index cc47fd4d34..64801f8e01 100644 --- a/src/test/test-strv.c +++ b/src/test/test-strv.c @@ -155,7 +155,7 @@ static void test_strv_join(void) { static void test_strv_quote_unquote(const char* const *split, const char *quoted) { _cleanup_free_ char *p; - _cleanup_strv_free_ char **s; + _cleanup_strv_free_ char **s = NULL; char **t; int r; @@ -166,7 +166,7 @@ static void test_strv_quote_unquote(const char* const *split, const char *quoted assert_se(streq(p, quoted)); r = strv_split_extract(&s, quoted, WHITESPACE, EXTRACT_QUOTES); - assert_se(r == 0); + assert_se(r == (int) strv_length(s)); assert_se(s); STRV_FOREACH(t, s) { assert_se(*t); @@ -183,7 +183,7 @@ static void test_strv_unquote(const char *quoted, char **list) { int r; r = strv_split_extract(&s, quoted, WHITESPACE, EXTRACT_QUOTES); - assert_se(r == 0); + assert_se(r == (int) strv_length(list)); assert_se(s); j = strv_join(s, " | "); assert_se(j); @@ -225,7 +225,7 @@ static void test_strv_split_extract(void) { int r; r = strv_split_extract(&l, str, ":", EXTRACT_DONT_COALESCE_SEPARATORS); - assert_se(r == 0); + assert_se(r == (int) strv_length(l)); assert_se(streq_ptr(l[0], "")); assert_se(streq_ptr(l[1], "foo:bar")); assert_se(streq_ptr(l[2], "")); @@ -591,6 +591,33 @@ static void test_strv_skip(void) { test_strv_skip_one(STRV_MAKE(NULL), 55, STRV_MAKE(NULL)); } +static void test_strv_extend_n(void) { + _cleanup_strv_free_ char **v = NULL; + + v = strv_new("foo", "bar", NULL); + assert_se(v); + + assert_se(strv_extend_n(&v, "waldo", 3) >= 0); + assert_se(strv_extend_n(&v, "piep", 2) >= 0); + + assert_se(streq(v[0], "foo")); + assert_se(streq(v[1], "bar")); + assert_se(streq(v[2], "waldo")); + assert_se(streq(v[3], "waldo")); + assert_se(streq(v[4], "waldo")); + assert_se(streq(v[5], "piep")); + assert_se(streq(v[6], "piep")); + assert_se(v[7] == NULL); + + v = strv_free(v); + + assert_se(strv_extend_n(&v, "foo", 1) >= 0); + assert_se(strv_extend_n(&v, "bar", 0) >= 0); + + assert_se(streq(v[0], "foo")); + assert_se(v[1] == NULL); +} + int main(int argc, char *argv[]) { test_specifier_printf(); test_strv_foreach(); @@ -650,6 +677,7 @@ int main(int argc, char *argv[]) { test_strv_reverse(); test_strv_shell_escape(); test_strv_skip(); + test_strv_extend_n(); return 0; } 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; } diff --git a/src/udev/net/link-config.c b/src/udev/net/link-config.c index 63e54db56e..4b8c5053a4 100644 --- a/src/udev/net/link-config.c +++ b/src/udev/net/link-config.c @@ -460,6 +460,7 @@ int link_config_apply(link_config_ctx *ctx, link_config *config, mac = &generated_mac; } break; + case MACPOLICY_NONE: default: mac = config->mac; } @@ -492,7 +493,8 @@ int link_get_driver(link_config_ctx *ctx, struct udev_device *device, char **ret static const char* const mac_policy_table[_MACPOLICY_MAX] = { [MACPOLICY_PERSISTENT] = "persistent", - [MACPOLICY_RANDOM] = "random" + [MACPOLICY_RANDOM] = "random", + [MACPOLICY_NONE] = "none" }; DEFINE_STRING_TABLE_LOOKUP(mac_policy, MACPolicy); diff --git a/src/udev/net/link-config.h b/src/udev/net/link-config.h index 9875057e84..c52db2ce55 100644 --- a/src/udev/net/link-config.h +++ b/src/udev/net/link-config.h @@ -32,6 +32,7 @@ typedef struct link_config link_config; typedef enum MACPolicy { MACPOLICY_PERSISTENT, MACPOLICY_RANDOM, + MACPOLICY_NONE, _MACPOLICY_MAX, _MACPOLICY_INVALID = -1 } MACPolicy; |