int namespace_flags_from_string(const char *name, unsigned long *ret) { unsigned long flags = 0; int r; assert_se(ret); for (;;) { _cleanup_free_ char *word = NULL; unsigned long f = 0; unsigned i; r = extract_first_word(&name, &word, NULL, 0); if (r < 0) return r; if (r == 0) break; for (i = 0; namespace_flag_map[i].name; i++) if (streq(word, namespace_flag_map[i].name)) { f = namespace_flag_map[i].flag; break; } if (f == 0) return -EINVAL; flags |= f; } *ret = flags; return 0; }
static int device_add_udev_wants(Unit *u, struct udev_device *dev) { const char *wants, *property, *p; int r; assert(u); assert(dev); property = MANAGER_IS_USER(u->manager) ? "SYSTEMD_USER_WANTS" : "SYSTEMD_WANTS"; wants = udev_device_get_property_value(dev, property); for (p = wants;;) { _cleanup_free_ char *word = NULL, *k = NULL; r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES); if (r == 0) return 0; if (r == -ENOMEM) return log_oom(); if (r < 0) return log_unit_error_errno(u, r, "Failed to add parse %s: %m", property); r = unit_name_mangle(word, UNIT_NAME_NOGLOB, &k); if (r < 0) return log_unit_error_errno(u, r, "Failed to mangle unit name \"%s\": %m", word); r = unit_add_dependency_by_name(u, UNIT_WANTS, k, NULL, true); if (r < 0) return log_unit_error_errno(u, r, "Failed to add wants dependency: %m"); } }
int namespace_flag_from_string_many(const char *name, unsigned long *ret) { unsigned long flags = 0; int r; assert_se(ret); if (!name) { *ret = 0; return 0; } for (;;) { _cleanup_free_ char *word = NULL; unsigned long f; r = extract_first_word(&name, &word, NULL, 0); if (r < 0) return r; if (r == 0) break; f = namespace_flag_from_string(word); if (f == 0) return -EINVAL; flags |= f; } *ret = flags; return 0; }
int secure_bits_from_string(const char *s) { int secure_bits = 0; const char *p; int r; for (p = s;;) { _cleanup_free_ char *word = NULL; r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES); if (r == -ENOMEM) return r; if (r <= 0) break; if (streq(word, "keep-caps")) secure_bits |= 1 << SECURE_KEEP_CAPS; else if (streq(word, "keep-caps-locked")) secure_bits |= 1 << SECURE_KEEP_CAPS_LOCKED; else if (streq(word, "no-setuid-fixup")) secure_bits |= 1 << SECURE_NO_SETUID_FIXUP; else if (streq(word, "no-setuid-fixup-locked")) secure_bits |= 1 << SECURE_NO_SETUID_FIXUP_LOCKED; else if (streq(word, "noroot")) secure_bits |= 1 << SECURE_NOROOT; else if (streq(word, "noroot-locked")) secure_bits |= 1 << SECURE_NOROOT_LOCKED; } return secure_bits; }
int get_proc_cmdline_key(const char *key, char **value) { _cleanup_free_ char *line = NULL, *ret = NULL; bool found = false; const char *p; int r; assert(key); r = proc_cmdline(&line); if (r < 0) return r; p = line; for (;;) { _cleanup_free_ char *word = NULL; const char *e; r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES|EXTRACT_RELAX); if (r < 0) return r; if (r == 0) break; /* Filter out arguments that are intended only for the * initrd */ if (!in_initrd() && startswith(word, "rd.")) continue; if (value) { e = startswith(word, key); if (!e) continue; r = free_and_strdup(&ret, e); if (r < 0) return r; found = true; } else { if (streq(word, key)) found = true; } } if (value) { *value = ret; ret = NULL; } return found; }
static int network_link_get_ifindexes(int ifindex, const char *key, int **ret) { char path[strlen("/run/systemd/netif/links/") + DECIMAL_STR_MAX(ifindex) + 1]; _cleanup_free_ int *ifis = NULL; _cleanup_free_ char *s = NULL; size_t allocated = 0, c = 0; const char *x; int r; assert_return(ifindex > 0, -EINVAL); assert_return(ret, -EINVAL); xsprintf(path, "/run/systemd/netif/links/%i", ifindex); r = parse_env_file(path, NEWLINE, key, &s, NULL); if (r == -ENOENT) return -ENODATA; if (r < 0) return r; if (isempty(s)) { *ret = NULL; return 0; } x = s; for (;;) { _cleanup_free_ char *word = NULL; r = extract_first_word(&x, &word, NULL, 0); if (r < 0) return r; if (r == 0) break; r = parse_ifindex(word, &ifindex); if (r < 0) return r; if (!GREEDY_REALLOC(ifis, allocated, c + 1)) return -ENOMEM; ifis[c++] = ifindex; } if (!GREEDY_REALLOC(ifis, allocated, c + 1)) return -ENOMEM; ifis[c] = 0; /* Let's add a 0 ifindex to the end, to be nice*/ *ret = ifis; ifis = NULL; return c; }
int parse_cpu_set_and_warn( const char *rvalue, cpu_set_t **cpu_set, const char *unit, const char *filename, unsigned line, const char *lvalue) { const char *whole_rvalue = rvalue; _cleanup_cpu_free_ cpu_set_t *c = NULL; unsigned ncpus = 0; assert(lvalue); assert(rvalue); for (;;) { _cleanup_free_ char *word = NULL; unsigned cpu; int r; r = extract_first_word(&rvalue, &word, WHITESPACE, EXTRACT_QUOTES); if (r < 0) { log_syntax(unit, LOG_ERR, filename, line, r, "Invalid value for %s: %s", lvalue, whole_rvalue); return r; } if (r == 0) break; if (!c) { c = cpu_set_malloc(&ncpus); if (!c) return log_oom(); } r = safe_atou(word, &cpu); if (r < 0 || cpu >= ncpus) { log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse CPU affinity '%s'", rvalue); return -EINVAL; } CPU_SET_S(cpu, CPU_ALLOC_SIZE(ncpus), c); } /* On success, sets *cpu_set and returns ncpus for the system. */ if (c) { *cpu_set = c; c = NULL; } return (int) ncpus; }
int config_parse_syscall_filter( 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) { Settings *settings = data; bool negative; const char *items; int r; assert(filename); assert(lvalue); assert(rvalue); negative = rvalue[0] == '~'; items = negative ? rvalue + 1 : rvalue; for (;;) { _cleanup_free_ char *word = NULL; r = extract_first_word(&items, &word, NULL, 0); if (r == 0) break; if (r == -ENOMEM) return log_oom(); if (r < 0) { log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse SystemCallFilter= parameter %s, ignoring: %m", rvalue); return 0; } if (negative) r = strv_extend(&settings->syscall_blacklist, word); else r = strv_extend(&settings->syscall_whitelist, word); if (r < 0) return log_oom(); } return 0; }
int config_parse_capability( 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) { uint64_t u = 0, *result = data; int r; assert(filename); assert(lvalue); assert(rvalue); for (;;) { _cleanup_free_ char *word = NULL; int cap; r = extract_first_word(&rvalue, &word, NULL, 0); if (r < 0) { log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract capability string, ignoring: %s", rvalue); return 0; } if (r == 0) break; cap = capability_from_name(word); if (cap < 0) { log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse capability, ignoring: %s", word); continue; } u |= 1 << ((uint64_t) cap); } if (u == 0) return 0; *result |= u; return 0; }
int proc_cmdline_parse(proc_cmdline_parse_t parse_item, void *data, unsigned flags) { _cleanup_free_ char *line = NULL; const char *p; int r; assert(parse_item); r = proc_cmdline(&line); if (r < 0) return r; p = line; for (;;) { _cleanup_free_ char *word = NULL; char *value, *key, *q; r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES|EXTRACT_RELAX); if (r < 0) return r; if (r == 0) break; key = word; /* Filter out arguments that are intended only for the initrd */ q = startswith(word, "rd."); if (q) { if (!in_initrd()) continue; if (flags & PROC_CMDLINE_STRIP_RD_PREFIX) key = q; } value = strchr(key, '='); if (value) *(value++) = 0; r = parse_item(key, value, data); if (r < 0) return r; } return 0; }
static int condition_test_kernel_command_line(Condition *c) { _cleanup_free_ char *line = NULL; const char *p; bool equal; int r; assert(c); assert(c->parameter); assert(c->type == CONDITION_KERNEL_COMMAND_LINE); r = proc_cmdline(&line); if (r < 0) return r; equal = !!strchr(c->parameter, '='); p = line; for (;;) { _cleanup_free_ char *word = NULL; bool found; r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES|EXTRACT_RELAX); if (r < 0) return r; if (r == 0) break; if (equal) found = streq(word, c->parameter); else { const char *f; f = startswith(word, c->parameter); found = f && (*f == '=' || *f == 0); } if (found) return true; } return false; }
int parse_proc_cmdline(int (*parse_item)(const char *key, const char *value, void *data), void *data, bool strip_prefix) { _cleanup_free_ char *line = NULL; const char *p; int r; assert(parse_item); r = proc_cmdline(&line); if (r < 0) return r; p = line; for (;;) { _cleanup_free_ char *word = NULL; char *value = NULL, *unprefixed; r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES|EXTRACT_RELAX); if (r < 0) return r; if (r == 0) break; /* Filter out arguments that are intended only for the * initrd */ unprefixed = startswith(word, "rd."); if (unprefixed && !in_initrd()) continue; value = strchr(word, '='); if (value) *(value++) = 0; r = parse_item(strip_prefix && unprefixed ? unprefixed : word, value, data); if (r < 0) return r; } return 0; }
static void check(const char *test, char** expected, bool trailing) { int i = 0, r; printf("<<<%s>>>\n", test); for (;;) { _cleanup_free_ char *word = NULL; r = extract_first_word(&test, &word, NULL, EXTRACT_QUOTES); if (r == 0) { assert_se(!trailing); break; } else if (r < 0) { assert_se(trailing); break; } assert_se(streq(word, expected[i++])); printf("<%s>\n", word); } assert_se(expected[i] == NULL); }
int manager_parse_server_string(Manager *m, ServerType type, const char *string) { ServerName *first; int r; assert(m); assert(string); first = type == SERVER_FALLBACK ? m->fallback_servers : m->system_servers; if (type == SERVER_FALLBACK) m->have_fallbacks = true; for (;;) { _cleanup_free_ char *word = NULL; bool found = false; ServerName *n; r = extract_first_word(&string, &word, NULL, 0); if (r < 0) return log_error_errno(r, "Failed to parse timesyncd server syntax \"%s\": %m", string); if (r == 0) break; /* Filter out duplicates */ LIST_FOREACH(names, n, first) if (streq_ptr(n->string, word)) { found = true; break; } if (found) continue; r = server_name_new(m, NULL, type, word); if (r < 0) return r; } return 0; }
int manager_parse_dns_server_string_and_warn(Manager *m, DnsServerType type, const char *string) { int r; assert(m); assert(string); for (;;) { _cleanup_free_ char *word = NULL; r = extract_first_word(&string, &word, NULL, 0); if (r < 0) return r; if (r == 0) break; r = manager_add_dns_server_by_string(m, type, word); if (r < 0) log_warning_errno(r, "Failed to add DNS server address '%s', ignoring: %m", word); } return 0; }
int manager_parse_search_domains_and_warn(Manager *m, const char *string) { int r; assert(m); assert(string); for (;;) { _cleanup_free_ char *word = NULL; r = extract_first_word(&string, &word, NULL, EXTRACT_QUOTES); if (r < 0) return r; if (r == 0) break; r = manager_add_search_domain_by_string(m, word); if (r < 0) log_warning_errno(r, "Failed to add search domain '%s', ignoring: %m", word); } return 0; }
/* Ignore failure. E.g. 10.M is valid */ frac = l2; for (; e < e2; e++) frac /= 10; } } e += strspn(e, WHITESPACE); for (i = start_pos; i < n_entries; i++) if (startswith(e, table[i].suffix)) break; if (i >= n_entries) return -EINVAL; if (l + (frac > 0) > ULLONG_MAX / table[i].factor) return -ERANGE; tmp = l * table[i].factor + (unsigned long long) (frac * table[i].factor); if (tmp > ULLONG_MAX - r) return -ERANGE; r += tmp; if ((unsigned long long) (uint64_t) r != r) return -ERANGE; p = e + strlen(table[i].suffix); start_pos = i + 1; } while (*p); *size = r; return 0; } #if 0 /* NM_IGNORED */ int parse_range(const char *t, unsigned *lower, unsigned *upper) { _cleanup_free_ char *word = NULL; unsigned l, u; int r; assert(lower); assert(upper); /* Extract the lower bound. */ r = extract_first_word(&t, &word, "-", EXTRACT_DONT_COALESCE_SEPARATORS); if (r < 0) return r; if (r == 0) return -EINVAL; r = safe_atou(word, &l); if (r < 0) return r; /* Check for the upper bound and extract it if needed */ if (!t) /* Single number with no dashes. */ u = l; else if (!*t) /* Trailing dash is an error. */ return -EINVAL; else { r = safe_atou(t, &u); if (r < 0) return r; } *lower = l; *upper = u; return 0; }
/* Mount a legacy cgroup hierarchy when cgroup namespaces are supported. */ static int mount_legacy_cgns_supported( const char *dest, CGroupUnified unified_requested, bool userns, uid_t uid_shift, uid_t uid_range, const char *selinux_apifs_context) { _cleanup_set_free_free_ Set *controllers = NULL; const char *cgroup_root = "/sys/fs/cgroup", *c; int r; (void) mkdir_p(cgroup_root, 0755); /* Mount a tmpfs to /sys/fs/cgroup if it's not mounted there yet. */ r = path_is_mount_point(cgroup_root, dest, AT_SYMLINK_FOLLOW); if (r < 0) return log_error_errno(r, "Failed to determine if /sys/fs/cgroup is already mounted: %m"); if (r == 0) { _cleanup_free_ char *options = NULL; /* When cgroup namespaces are enabled and user namespaces are * used then the mount of the cgroupfs is done *inside* the new * user namespace. We're root in the new user namespace and the * kernel will happily translate our uid/gid to the correct * uid/gid as seen from e.g. /proc/1/mountinfo. So we simply * pass uid 0 and not uid_shift to tmpfs_patch_options(). */ r = tmpfs_patch_options("mode=755", 0, selinux_apifs_context, &options); if (r < 0) return log_oom(); r = mount_verbose(LOG_ERR, "tmpfs", cgroup_root, "tmpfs", MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME, options); if (r < 0) return r; } r = cg_all_unified(); if (r < 0) return r; if (r > 0) goto skip_controllers; r = get_process_controllers(&controllers); if (r < 0) return log_error_errno(r, "Failed to determine cgroup controllers: %m"); for (;;) { _cleanup_free_ const char *controller = NULL; controller = set_steal_first(controllers); if (!controller) break; r = mount_legacy_cgroup_hierarchy("", controller, controller, !userns); if (r < 0) return r; /* When multiple hierarchies are co-mounted, make their * constituting individual hierarchies a symlink to the * co-mount. */ c = controller; for (;;) { _cleanup_free_ char *target = NULL, *tok = NULL; r = extract_first_word(&c, &tok, ",", 0); if (r < 0) return log_error_errno(r, "Failed to extract co-mounted cgroup controller: %m"); if (r == 0) break; if (streq(controller, tok)) break; target = prefix_root("/sys/fs/cgroup/", tok); if (!target) return log_oom(); r = symlink_idempotent(controller, target, false); if (r == -EINVAL) return log_error_errno(r, "Invalid existing symlink for combined hierarchy: %m"); if (r < 0) return log_error_errno(r, "Failed to create symlink for combined hierarchy: %m"); } } skip_controllers: if (unified_requested >= CGROUP_UNIFIED_SYSTEMD) { r = mount_legacy_cgroup_hierarchy("", SYSTEMD_CGROUP_CONTROLLER_HYBRID, "unified", false); if (r < 0) return r; } r = mount_legacy_cgroup_hierarchy("", SYSTEMD_CGROUP_CONTROLLER_LEGACY, "systemd", false); if (r < 0) return r; if (!userns) return mount_verbose(LOG_ERR, NULL, cgroup_root, NULL, MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME|MS_RDONLY, "mode=755"); return 0; }
int ethtool_set_channels(int *fd, const char *ifname, netdev_channels *channels) { struct ethtool_channels ecmd = { .cmd = ETHTOOL_GCHANNELS }; struct ifreq ifr = { .ifr_data = (void*) &ecmd }; bool need_update = false; int r; if (*fd < 0) { r = ethtool_connect(fd); if (r < 0) return log_warning_errno(r, "link_config: could not connect to ethtool: %m"); } strscpy(ifr.ifr_name, IFNAMSIZ, ifname); r = ioctl(*fd, SIOCETHTOOL, &ifr); if (r < 0) return -errno; if (channels->rx_count_set && ecmd.rx_count != channels->rx_count) { ecmd.rx_count = channels->rx_count; need_update = true; } if (channels->tx_count_set && ecmd.tx_count != channels->tx_count) { ecmd.tx_count = channels->tx_count; need_update = true; } if (channels->other_count_set && ecmd.other_count != channels->other_count) { ecmd.other_count = channels->other_count; need_update = true; } if (channels->combined_count_set && ecmd.combined_count != channels->combined_count) { ecmd.combined_count = channels->combined_count; need_update = true; } if (need_update) { ecmd.cmd = ETHTOOL_SCHANNELS; r = ioctl(*fd, SIOCETHTOOL, &ifr); if (r < 0) return -errno; } return 0; } int config_parse_advertise(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) { link_config *config = data; const char *p; int r; assert(filename); assert(section); assert(lvalue); assert(rvalue); assert(data); if (isempty(rvalue)) { /* Empty string resets the value. */ zero(config->advertise); return 0; } for (p = rvalue;;) { _cleanup_free_ char *w = NULL; enum ethtool_link_mode_bit_indices mode; r = extract_first_word(&p, &w, NULL, 0); if (r == -ENOMEM) return log_oom(); if (r < 0) { log_syntax(unit, LOG_ERR, filename, line, r, "Failed to split advertise modes '%s', ignoring: %m", rvalue); break; } if (r == 0) break; mode = ethtool_link_mode_bit_from_string(w); /* We reuse the kernel provided enum which does not contain negative value. So, the cast * below is mandatory. Otherwise, the check below always passes and access an invalid address. */ if ((int) mode < 0) { log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse advertise mode, ignoring: %s", w); continue; } config->advertise[mode / 32] |= 1UL << (mode % 32); } return 0; }
int config_parse_ip_address_access( 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) { IPAddressAccessItem **list = data; const char *p; int r; assert(list); if (isempty(rvalue)) { *list = ip_address_access_free_all(*list); return 0; } p = rvalue; for (;;) { _cleanup_free_ IPAddressAccessItem *a = NULL; _cleanup_free_ char *word = NULL; r = extract_first_word(&p, &word, NULL, 0); if (r == 0) break; if (r == -ENOMEM) return log_oom(); if (r < 0) { log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", rvalue); break; } a = new0(IPAddressAccessItem, 1); if (!a) return log_oom(); if (streq(word, "any")) { /* "any" is a shortcut for 0.0.0.0/0 and ::/0 */ a->family = AF_INET; LIST_APPEND(items, *list, a); a = new0(IPAddressAccessItem, 1); if (!a) return log_oom(); a->family = AF_INET6; } else if (is_localhost(word)) { /* "localhost" is a shortcut for 127.0.0.0/8 and ::1/128 */ a->family = AF_INET; a->address.in.s_addr = htobe32(0x7f000000); a->prefixlen = 8; LIST_APPEND(items, *list, a); a = new0(IPAddressAccessItem, 1); if (!a) return log_oom(); a->family = AF_INET6; a->address.in6 = (struct in6_addr) IN6ADDR_LOOPBACK_INIT; a->prefixlen = 128; } else if (streq(word, "link-local")) { /* "link-local" is a shortcut for 169.254.0.0/16 and fe80::/64 */ a->family = AF_INET; a->address.in.s_addr = htobe32((UINT32_C(169) << 24 | UINT32_C(254) << 16)); a->prefixlen = 16; LIST_APPEND(items, *list, a); a = new0(IPAddressAccessItem, 1); if (!a) return log_oom(); a->family = AF_INET6; a->address.in6 = (struct in6_addr) { .s6_addr32[0] = htobe32(0xfe800000) }; a->prefixlen = 64; } else if (streq(word, "multicast")) { /* "multicast" is a shortcut for 224.0.0.0/4 and ff00::/8 */ a->family = AF_INET; a->address.in.s_addr = htobe32((UINT32_C(224) << 24)); a->prefixlen = 4; LIST_APPEND(items, *list, a); a = new0(IPAddressAccessItem, 1); if (!a) return log_oom(); a->family = AF_INET6; a->address.in6 = (struct in6_addr) { .s6_addr32[0] = htobe32(0xff000000) }; a->prefixlen = 8; } else {
int ethtool_set_channels(int *fd, const char *ifname, netdev_channels *channels) { struct ethtool_channels ecmd = { .cmd = ETHTOOL_GCHANNELS }; struct ifreq ifr = { .ifr_data = (void*) &ecmd }; bool need_update = false; int r; if (*fd < 0) { r = ethtool_connect(fd); if (r < 0) return log_warning_errno(r, "link_config: could not connect to ethtool: %m"); } strscpy(ifr.ifr_name, IFNAMSIZ, ifname); r = ioctl(*fd, SIOCETHTOOL, &ifr); if (r < 0) return -errno; if (channels->rx_count_set && ecmd.rx_count != channels->rx_count) { ecmd.rx_count = channels->rx_count; need_update = true; } if (channels->tx_count_set && ecmd.tx_count != channels->tx_count) { ecmd.tx_count = channels->tx_count; need_update = true; } if (channels->other_count_set && ecmd.other_count != channels->other_count) { ecmd.other_count = channels->other_count; need_update = true; } if (channels->combined_count_set && ecmd.combined_count != channels->combined_count) { ecmd.combined_count = channels->combined_count; need_update = true; } if (need_update) { ecmd.cmd = ETHTOOL_SCHANNELS; r = ioctl(*fd, SIOCETHTOOL, &ifr); if (r < 0) return -errno; } return 0; } int config_parse_advertise(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) { link_config *config = data; NetDevAdvertise mode, a = 0; const char *p; int r; assert(filename); assert(section); assert(lvalue); assert(rvalue); assert(data); if (isempty(rvalue)) { /* Empty string resets the value. */ config->advertise = 0; return 0; } for (p = rvalue;;) { _cleanup_free_ char *w = NULL; r = extract_first_word(&p, &w, NULL, 0); if (r == -ENOMEM) return log_oom(); if (r < 0) { log_syntax(unit, LOG_ERR, filename, line, r, "Failed to split advertise modes '%s', ignoring: %m", rvalue); break; } if (r == 0) break; mode = advertise_from_string(w); if (mode == _NET_DEV_ADVERTISE_INVALID) { log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse advertise mode, ignoring: %s", w); continue; } a |= mode; } config->advertise |= a; return 0; }
int config_parse_duid_rawdata( 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 r, n1, n2, byte; char *cbyte; const char *pduid = rvalue; Manager *m = userdata; Network *n = userdata; DUIDType duidtype; uint16_t dhcp_duid_type = 0; uint8_t dhcp_duid[MAX_DUID_LEN]; size_t len, count = 0, duid_start_offset = 0, dhcp_duid_len = 0; assert(filename); assert(lvalue); assert(rvalue); assert(userdata); duidtype = (ltype == DUID_CONFIG_SOURCE_GLOBAL) ? m->duid_type : n->duid_type; if (duidtype == _DUID_TYPE_INVALID) duidtype = DUID_TYPE_RAW; switch (duidtype) { case DUID_TYPE_LLT: /* RawData contains DUID-LLT link-layer address (offset 6) */ duid_start_offset = 6; break; case DUID_TYPE_EN: /* RawData contains DUID-EN identifier (offset 4) */ duid_start_offset = 4; break; case DUID_TYPE_LL: /* RawData contains DUID-LL link-layer address (offset 2) */ duid_start_offset = 2; break; case DUID_TYPE_UUID: /* RawData specifies UUID (offset 0) - fall thru */ case DUID_TYPE_RAW: /* First two bytes of RawData is DUID Type - fall thru */ default: break; } if (duidtype != DUID_TYPE_RAW) dhcp_duid_type = (uint16_t)duidtype; /* RawData contains DUID in format " NN:NN:NN... " */ for (;;) { r = extract_first_word(&pduid, &cbyte, ":", 0); if (r < 0) { log_syntax(unit, LOG_ERR, filename, line, r, "Failed to read DUID, ignoring assignment: %s.", rvalue); goto exit; } if (r == 0) break; if ((duid_start_offset + dhcp_duid_len) >= MAX_DUID_LEN) { log_syntax(unit, LOG_ERR, filename, line, 0, "Max DUID length exceeded, ignoring assignment: %s.", rvalue); goto exit; } len = strlen(cbyte); if ((len == 0) || (len > 2)) { log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid length - DUID byte: %s, ignoring assignment: %s.", cbyte, rvalue); goto exit; } n2 = 0; n1 = unhexchar(cbyte[0]); if (len == 2) n2 = unhexchar(cbyte[1]); if ((n1 < 0) || (n2 < 0)) { log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid DUID byte: %s. Ignoring assignment: %s.", cbyte, rvalue); goto exit; } byte = (n1 << (4 * (len-1))) | n2; /* If DUID_TYPE_RAW, first two bytes hold DHCP DUID type code */ if ((duidtype == DUID_TYPE_RAW) && (count < 2)) { dhcp_duid_type |= (byte << (8 * (1 - count))); count++; continue; } dhcp_duid[duid_start_offset + dhcp_duid_len] = byte; dhcp_duid_len++; } if (ltype == DUID_CONFIG_SOURCE_GLOBAL) { m->duid_type = duidtype; m->dhcp_duid_type = dhcp_duid_type; m->dhcp_duid_len = dhcp_duid_len; memcpy(&m->dhcp_duid[duid_start_offset], dhcp_duid, dhcp_duid_len); } else { /* DUID_CONFIG_SOURCE_NETWORK */ n->duid_type = duidtype; n->dhcp_duid_type = dhcp_duid_type; n->dhcp_duid_len = dhcp_duid_len; memcpy(&n->dhcp_duid[duid_start_offset], dhcp_duid, dhcp_duid_len); } exit: return 0; }
int proc_cmdline_get_key(const char *key, unsigned flags, char **value) { _cleanup_free_ char *line = NULL, *ret = NULL; bool found = false; const char *p; int r; /* Looks for a specific key on the kernel command line. Supports two modes: * * a) The "value" parameter is used. In this case a parameter beginning with the "key" string followed by "=" * is searched, and the value following this is returned in "value". * * b) as above, but the PROC_CMDLINE_VALUE_OPTIONAL flag is set. In this case if the key is found as a * separate word (i.e. not followed by "=" but instead by whitespace or the end of the command line), then * this is also accepted, and "value" is returned as NULL. * * c) The "value" parameter is NULL. In this case a search for the exact "key" parameter is performed. * * In all three cases, > 0 is returned if the key is found, 0 if not. */ if (isempty(key)) return -EINVAL; if ((flags & PROC_CMDLINE_VALUE_OPTIONAL) && !value) return -EINVAL; r = proc_cmdline(&line); if (r < 0) return r; p = line; for (;;) { _cleanup_free_ char *word = NULL; const char *e; r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES|EXTRACT_RELAX); if (r < 0) return r; if (r == 0) break; /* Automatically filter out arguments that are intended only for the initrd, if we are not in the * initrd. */ if (!in_initrd() && startswith(word, "rd.")) continue; if (value) { e = proc_cmdline_key_startswith(word, key); if (!e) continue; if (*e == '=') { r = free_and_strdup(&ret, e+1); if (r < 0) return r; found = true; } else if (*e == 0 && (flags & PROC_CMDLINE_VALUE_OPTIONAL)) found = true; } else { if (streq(word, key)) found = true; } } if (value) *value = TAKE_PTR(ret); return found; }
int config_parse_duid_rawdata( 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) { DUID *ret = data; uint8_t raw_data[MAX_DUID_LEN]; unsigned count = 0; assert(filename); assert(lvalue); assert(rvalue); assert(ret); /* RawData contains DUID in format "NN:NN:NN..." */ for (;;) { int n1, n2, len, r; uint32_t byte; _cleanup_free_ char *cbyte = NULL; r = extract_first_word(&rvalue, &cbyte, ":", 0); if (r < 0) { log_syntax(unit, LOG_ERR, filename, line, r, "Failed to read DUID, ignoring assignment: %s.", rvalue); return 0; } if (r == 0) break; if (count >= MAX_DUID_LEN) { log_syntax(unit, LOG_ERR, filename, line, 0, "Max DUID length exceeded, ignoring assignment: %s.", rvalue); return 0; } len = strlen(cbyte); if (!IN_SET(len, 1, 2)) { log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid length - DUID byte: %s, ignoring assignment: %s.", cbyte, rvalue); return 0; } n1 = unhexchar(cbyte[0]); if (len == 2) n2 = unhexchar(cbyte[1]); else n2 = 0; if (n1 < 0 || n2 < 0) { log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid DUID byte: %s. Ignoring assignment: %s.", cbyte, rvalue); return 0; } byte = ((uint8_t) n1 << (4 * (len-1))) | (uint8_t) n2; raw_data[count++] = byte; } assert_cc(sizeof(raw_data) == sizeof(ret->raw_data)); memcpy(ret->raw_data, raw_data, count); ret->raw_data_len = count; return 0; }
int config_parse_dnssd_txt(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_(dnssd_txtdata_freep) DnssdTxtData *txt_data = NULL; DnssdService *s = userdata; DnsTxtItem *last = NULL; assert(filename); assert(lvalue); assert(rvalue); assert(s); if (isempty(rvalue)) { /* Flush out collected items */ s->txt_data_items = dnssd_txtdata_free_all(s->txt_data_items); return 0; } txt_data = new0(DnssdTxtData, 1); if (!txt_data) return log_oom(); for (;;) { _cleanup_free_ char *word = NULL; _cleanup_free_ char *key = NULL; _cleanup_free_ char *value = NULL; _cleanup_free_ void *decoded = NULL; size_t length = 0; DnsTxtItem *i; int r; r = extract_first_word(&rvalue, &word, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE|EXTRACT_CUNESCAPE_RELAX); if (r == 0) break; if (r == -ENOMEM) return log_oom(); if (r < 0) return log_syntax(unit, LOG_ERR, filename, line, r, "Invalid syntax, ignoring: %s", rvalue); r = split_pair(word, "=", &key, &value); if (r == -ENOMEM) return log_oom(); if (r == -EINVAL) key = TAKE_PTR(word); if (!ascii_is_valid(key)) { log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid syntax, ignoring: %s", key); return -EINVAL; } switch (ltype) { case DNS_TXT_ITEM_DATA: if (value) { r = unbase64mem(value, strlen(value), &decoded, &length); if (r == -ENOMEM) return log_oom(); if (r < 0) return log_syntax(unit, LOG_ERR, filename, line, r, "Invalid base64 encoding, ignoring: %s", value); } r = dnssd_txt_item_new_from_data(key, decoded, length, &i); if (r < 0) return log_oom(); break; case DNS_TXT_ITEM_TEXT: r = dnssd_txt_item_new_from_string(key, value, &i); if (r < 0) return log_oom(); break; default: assert_not_reached("Unknown type of Txt config"); } LIST_INSERT_AFTER(items, txt_data->txt, last, i); last = i; } if (!LIST_IS_EMPTY(txt_data->txt)) { LIST_PREPEND(items, s->txt_data_items, txt_data); txt_data = NULL; } return 0; }