build: update systemd code
This is a direct dump from systemd git on 2015-04-08, git commit 431c3b6bab9c; it updates the existing files and imports systemd IPv4LL implementation. SYSTEMD_DIR=../systemd COMMIT=431c3b6bab9ceb54cd144d8df24d764a8a5f8fcc ( cd "$SYSTEMD_DIR" git checkout "$COMMIT" git reset --hard git clean -fdx ) /bin/cp "$SYSTEMD_DIR"/src/libsystemd/sd-id128/sd-id128.c ./src/systemd/src/libsystemd/sd-id128/sd-id128.c /bin/cp "$SYSTEMD_DIR"/src/libsystemd-network/dhcp-identifier.c ./src/systemd/src/libsystemd-network/dhcp-identifier.c /bin/cp "$SYSTEMD_DIR"/src/libsystemd-network/dhcp-identifier.h ./src/systemd/src/libsystemd-network/dhcp-identifier.h /bin/cp "$SYSTEMD_DIR"/src/libsystemd-network/dhcp-internal.h ./src/systemd/src/libsystemd-network/dhcp-internal.h /bin/cp "$SYSTEMD_DIR"/src/libsystemd-network/dhcp-lease-internal.h ./src/systemd/src/libsystemd-network/dhcp-lease-internal.h /bin/cp "$SYSTEMD_DIR"/src/libsystemd-network/dhcp-network.c ./src/systemd/src/libsystemd-network/dhcp-network.c /bin/cp "$SYSTEMD_DIR"/src/libsystemd-network/dhcp-option.c ./src/systemd/src/libsystemd-network/dhcp-option.c /bin/cp "$SYSTEMD_DIR"/src/libsystemd-network/dhcp-packet.c ./src/systemd/src/libsystemd-network/dhcp-packet.c /bin/cp "$SYSTEMD_DIR"/src/libsystemd-network/dhcp-protocol.h ./src/systemd/src/libsystemd-network/dhcp-protocol.h /bin/cp "$SYSTEMD_DIR"/src/libsystemd-network/dhcp6-internal.h ./src/systemd/src/libsystemd-network/dhcp6-internal.h /bin/cp "$SYSTEMD_DIR"/src/libsystemd-network/dhcp6-lease-internal.h ./src/systemd/src/libsystemd-network/dhcp6-lease-internal.h /bin/cp "$SYSTEMD_DIR"/src/libsystemd-network/dhcp6-network.c ./src/systemd/src/libsystemd-network/dhcp6-network.c /bin/cp "$SYSTEMD_DIR"/src/libsystemd-network/dhcp6-option.c ./src/systemd/src/libsystemd-network/dhcp6-option.c /bin/cp "$SYSTEMD_DIR"/src/libsystemd-network/dhcp6-protocol.h ./src/systemd/src/libsystemd-network/dhcp6-protocol.h /bin/cp "$SYSTEMD_DIR"/src/libsystemd-network/network-internal.c ./src/systemd/src/libsystemd-network/network-internal.c /bin/cp "$SYSTEMD_DIR"/src/libsystemd-network/network-internal.h ./src/systemd/src/libsystemd-network/network-internal.h /bin/cp "$SYSTEMD_DIR"/src/libsystemd-network/sd-dhcp-client.c ./src/systemd/src/libsystemd-network/sd-dhcp-client.c /bin/cp "$SYSTEMD_DIR"/src/libsystemd-network/sd-dhcp-lease.c ./src/systemd/src/libsystemd-network/sd-dhcp-lease.c /bin/cp "$SYSTEMD_DIR"/src/libsystemd-network/sd-dhcp6-client.c ./src/systemd/src/libsystemd-network/sd-dhcp6-client.c /bin/cp "$SYSTEMD_DIR"/src/libsystemd-network/sd-dhcp6-lease.c ./src/systemd/src/libsystemd-network/sd-dhcp6-lease.c /bin/cp "$SYSTEMD_DIR"/src/libsystemd-network/ipv4ll-internal.h ./src/systemd/src/libsystemd-network/ipv4ll-internal.h /bin/cp "$SYSTEMD_DIR"/src/libsystemd-network/ipv4ll-internal.h ./src/systemd/src/libsystemd-network/ipv4ll-internal.h /bin/cp "$SYSTEMD_DIR"/src/libsystemd-network/ipv4ll-network.c ./src/systemd/src/libsystemd-network/ipv4ll-network.c /bin/cp "$SYSTEMD_DIR"/src/libsystemd-network//ipv4ll-packet.c ./src/systemd/src/libsystemd-network//ipv4ll-packet.c /bin/cp "$SYSTEMD_DIR"/src/libsystemd-network/sd-ipv4ll.c ./src/systemd/src/libsystemd-network/sd-ipv4ll.c /bin/cp "$SYSTEMD_DIR"/src/shared/async.h ./src/systemd/src/shared/async.h /bin/cp "$SYSTEMD_DIR"/src/shared/fileio.c ./src/systemd/src/shared/fileio.c /bin/cp "$SYSTEMD_DIR"/src/shared/fileio.h ./src/systemd/src/shared/fileio.h /bin/cp "$SYSTEMD_DIR"/src/shared/list.h ./src/systemd/src/shared/list.h /bin/cp "$SYSTEMD_DIR"/src/shared/log.h ./src/systemd/src/shared/log.h /bin/cp "$SYSTEMD_DIR"/src/shared/macro.h ./src/systemd/src/shared/macro.h /bin/cp "$SYSTEMD_DIR"/src/shared/path-util.c ./src/systemd/src/shared/path-util.c /bin/cp "$SYSTEMD_DIR"/src/shared/path-util.h ./src/systemd/src/shared/path-util.h /bin/cp "$SYSTEMD_DIR"/src/shared/refcnt.h ./src/systemd/src/shared/refcnt.h /bin/cp "$SYSTEMD_DIR"/src/shared/siphash24.c ./src/systemd/src/shared/siphash24.c /bin/cp "$SYSTEMD_DIR"/src/shared/siphash24.h ./src/systemd/src/shared/siphash24.h /bin/cp "$SYSTEMD_DIR"/src/shared/socket-util.h ./src/systemd/src/shared/socket-util.h /bin/cp "$SYSTEMD_DIR"/src/shared/sparse-endian.h ./src/systemd/src/shared/sparse-endian.h /bin/cp "$SYSTEMD_DIR"/src/shared/strv.c ./src/systemd/src/shared/strv.c /bin/cp "$SYSTEMD_DIR"/src/shared/strv.h ./src/systemd/src/shared/strv.h /bin/cp "$SYSTEMD_DIR"/src/shared/time-util.c ./src/systemd/src/shared/time-util.c /bin/cp "$SYSTEMD_DIR"/src/shared/time-util.h ./src/systemd/src/shared/time-util.h /bin/cp "$SYSTEMD_DIR"/src/shared/utf8.c ./src/systemd/src/shared/utf8.c /bin/cp "$SYSTEMD_DIR"/src/shared/utf8.h ./src/systemd/src/shared/utf8.h /bin/cp "$SYSTEMD_DIR"/src/shared/util.c ./src/systemd/src/shared/util.c /bin/cp "$SYSTEMD_DIR"/src/shared/util.h ./src/systemd/src/shared/util.h /bin/cp "$SYSTEMD_DIR"/src/shared/unaligned.h ./src/systemd/src/shared/unaligned.h /bin/cp "$SYSTEMD_DIR"/src/shared/in-addr-util.c ./src/systemd/src/shared/in-addr-util.c /bin/cp "$SYSTEMD_DIR"/src/shared/in-addr-util.h ./src/systemd/src/shared/in-addr-util.h /bin/cp "$SYSTEMD_DIR"/src/systemd/_sd-common.h ./src/systemd/src/systemd/_sd-common.h /bin/cp "$SYSTEMD_DIR"/src/systemd/sd-dhcp-client.h ./src/systemd/src/systemd/sd-dhcp-client.h /bin/cp "$SYSTEMD_DIR"/src/systemd/sd-dhcp-lease.h ./src/systemd/src/systemd/sd-dhcp-lease.h /bin/cp "$SYSTEMD_DIR"/src/systemd/sd-dhcp6-client.h ./src/systemd/src/systemd/sd-dhcp6-client.h /bin/cp "$SYSTEMD_DIR"/src/systemd/sd-dhcp6-lease.h ./src/systemd/src/systemd/sd-dhcp6-lease.h /bin/cp "$SYSTEMD_DIR"/src/systemd/sd-event.h ./src/systemd/src/systemd/sd-event.h /bin/cp "$SYSTEMD_DIR"/src/systemd/sd-id128.h ./src/systemd/src/systemd/sd-id128.h /bin/cp "$SYSTEMD_DIR"/src/systemd/sd-ipv4ll.h ./src/systemd/src/systemd/sd-ipv4ll.h
This commit is contained in:
38
src/systemd/src/libsystemd-network/ipv4ll-internal.h
Normal file
38
src/systemd/src/libsystemd-network/ipv4ll-internal.h
Normal file
@@ -0,0 +1,38 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/***
|
||||
This file is part of systemd.
|
||||
|
||||
Copyright (C) 2014 Axis Communications AB. All rights reserved.
|
||||
|
||||
systemd is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
systemd is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#include <netinet/if_ether.h>
|
||||
|
||||
#include "sparse-endian.h"
|
||||
#include "socket-util.h"
|
||||
|
||||
int arp_network_bind_raw_socket(int index, union sockaddr_union *link);
|
||||
int arp_network_send_raw_socket(int fd, const union sockaddr_union *link,
|
||||
const struct ether_arp *arp);
|
||||
|
||||
void arp_packet_init(struct ether_arp *arp);
|
||||
void arp_packet_probe(struct ether_arp *arp, be32_t pa, const struct ether_addr *ha);
|
||||
void arp_packet_announcement(struct ether_arp *arp, be32_t pa, const struct ether_addr *ha);
|
||||
int arp_packet_verify_headers(struct ether_arp *arp);
|
||||
|
||||
#define log_ipv4ll(ll, fmt, ...) log_internal(LOG_DEBUG, 0, __FILE__, __LINE__, __func__, "IPv4LL: " fmt, ##__VA_ARGS__)
|
91
src/systemd/src/libsystemd-network/ipv4ll-network.c
Normal file
91
src/systemd/src/libsystemd-network/ipv4ll-network.c
Normal file
@@ -0,0 +1,91 @@
|
||||
/***
|
||||
This file is part of systemd.
|
||||
|
||||
Copyright (C) 2014 Axis Communications AB. All rights reserved.
|
||||
|
||||
systemd is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
systemd is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#include <linux/filter.h>
|
||||
|
||||
#include "util.h"
|
||||
#include "ipv4ll-internal.h"
|
||||
|
||||
int arp_network_send_raw_socket(int fd, const union sockaddr_union *link,
|
||||
const struct ether_arp *arp) {
|
||||
int r;
|
||||
|
||||
assert(arp);
|
||||
assert(link);
|
||||
assert(fd >= 0);
|
||||
|
||||
r = sendto(fd, arp, sizeof(struct ether_arp), 0, &link->sa, sizeof(link->ll));
|
||||
if (r < 0)
|
||||
return -errno;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int arp_network_bind_raw_socket(int ifindex, union sockaddr_union *link) {
|
||||
|
||||
static const struct sock_filter filter[] = {
|
||||
BPF_STMT(BPF_LD + BPF_W + BPF_LEN, 0), /* A <- packet length */
|
||||
BPF_JUMP(BPF_JMP + BPF_JGE + BPF_K, sizeof(struct ether_arp), 1, 0), /* packet >= arp packet ? */
|
||||
BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
|
||||
BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_hrd)), /* A <- header */
|
||||
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPHRD_ETHER, 1, 0), /* header == ethernet ? */
|
||||
BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
|
||||
BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_pro)), /* A <- protocol */
|
||||
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 1, 0), /* protocol == IP ? */
|
||||
BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
|
||||
BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, ea_hdr.ar_op)), /* A <- operation */
|
||||
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REQUEST, 0, 1), /* protocol == request ? */
|
||||
BPF_STMT(BPF_RET + BPF_K, 65535), /* return all */
|
||||
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REPLY, 0, 1), /* protocol == reply ? */
|
||||
BPF_STMT(BPF_RET + BPF_K, 65535), /* return all */
|
||||
BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
|
||||
};
|
||||
struct sock_fprog fprog = {
|
||||
.len = ELEMENTSOF(filter),
|
||||
.filter = (struct sock_filter*) filter
|
||||
};
|
||||
_cleanup_close_ int s = -1;
|
||||
int r;
|
||||
|
||||
assert(ifindex > 0);
|
||||
assert(link);
|
||||
|
||||
s = socket(PF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
|
||||
if (s < 0)
|
||||
return -errno;
|
||||
|
||||
r = setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog));
|
||||
if (r < 0)
|
||||
return -errno;
|
||||
|
||||
link->ll.sll_family = AF_PACKET;
|
||||
link->ll.sll_protocol = htons(ETH_P_ARP);
|
||||
link->ll.sll_ifindex = ifindex;
|
||||
link->ll.sll_halen = ETH_ALEN;
|
||||
memset(link->ll.sll_addr, 0xff, ETH_ALEN);
|
||||
|
||||
r = bind(s, &link->sa, sizeof(link->ll));
|
||||
if (r < 0)
|
||||
return -errno;
|
||||
|
||||
r = s;
|
||||
s = -1;
|
||||
|
||||
return r;
|
||||
}
|
71
src/systemd/src/libsystemd-network/ipv4ll-packet.c
Normal file
71
src/systemd/src/libsystemd-network/ipv4ll-packet.c
Normal file
@@ -0,0 +1,71 @@
|
||||
/***
|
||||
This file is part of systemd.
|
||||
|
||||
Copyright (C) 2014 Axis Communications AB. All rights reserved.
|
||||
|
||||
systemd is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
systemd is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include "util.h"
|
||||
#include "ipv4ll-internal.h"
|
||||
|
||||
void arp_packet_init(struct ether_arp *arp) {
|
||||
assert(arp);
|
||||
|
||||
memzero(arp, sizeof(struct ether_arp));
|
||||
/* Header */
|
||||
arp->ea_hdr.ar_hrd = htons(ARPHRD_ETHER); /* HTYPE */
|
||||
arp->ea_hdr.ar_pro = htons(ETHERTYPE_IP); /* PTYPE */
|
||||
arp->ea_hdr.ar_hln = ETH_ALEN; /* HLEN */
|
||||
arp->ea_hdr.ar_pln = sizeof arp->arp_spa; /* PLEN */
|
||||
arp->ea_hdr.ar_op = htons(ARPOP_REQUEST); /* REQUEST */
|
||||
}
|
||||
|
||||
void arp_packet_probe(struct ether_arp *arp, be32_t pa, const struct ether_addr *ha) {
|
||||
assert(ha);
|
||||
|
||||
arp_packet_init(arp);
|
||||
memcpy(arp->arp_sha, ha, ETH_ALEN);
|
||||
memcpy(arp->arp_tpa, &pa, sizeof(pa));
|
||||
}
|
||||
|
||||
void arp_packet_announcement(struct ether_arp *arp, be32_t pa, const struct ether_addr *ha) {
|
||||
assert(ha);
|
||||
|
||||
arp_packet_init(arp);
|
||||
memcpy(arp->arp_sha, ha, ETH_ALEN);
|
||||
memcpy(arp->arp_tpa, &pa, sizeof(pa));
|
||||
memcpy(arp->arp_spa, &pa, sizeof(pa));
|
||||
}
|
||||
|
||||
int arp_packet_verify_headers(struct ether_arp *arp) {
|
||||
assert(arp);
|
||||
|
||||
if (arp->ea_hdr.ar_hrd != htons(ARPHRD_ETHER)) {
|
||||
log_ipv4ll(NULL, "ignoring packet: header is not ARPHRD_ETHER");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (arp->ea_hdr.ar_pro != htons(ETHERTYPE_IP)) {
|
||||
log_ipv4ll(NULL, "ignoring packet: protocol is not ETHERTYPE_IP");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (arp->ea_hdr.ar_op != htons(ARPOP_REQUEST) &&
|
||||
arp->ea_hdr.ar_op != htons(ARPOP_REPLY)) {
|
||||
log_ipv4ll(NULL, "ignoring packet: operation is not ARPOP_REQUEST or ARPOP_REPLY");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
648
src/systemd/src/libsystemd-network/sd-ipv4ll.c
Normal file
648
src/systemd/src/libsystemd-network/sd-ipv4ll.c
Normal file
@@ -0,0 +1,648 @@
|
||||
/***
|
||||
This file is part of systemd.
|
||||
|
||||
Copyright (C) 2014 Axis Communications AB. All rights reserved.
|
||||
|
||||
systemd is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
systemd is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include "util.h"
|
||||
#include "siphash24.h"
|
||||
#include "list.h"
|
||||
#include "refcnt.h"
|
||||
|
||||
#include "ipv4ll-internal.h"
|
||||
#include "sd-ipv4ll.h"
|
||||
|
||||
/* Constants from the RFC */
|
||||
#define PROBE_WAIT 1
|
||||
#define PROBE_NUM 3
|
||||
#define PROBE_MIN 1
|
||||
#define PROBE_MAX 2
|
||||
#define ANNOUNCE_WAIT 2
|
||||
#define ANNOUNCE_NUM 2
|
||||
#define ANNOUNCE_INTERVAL 2
|
||||
#define MAX_CONFLICTS 10
|
||||
#define RATE_LIMIT_INTERVAL 60
|
||||
#define DEFEND_INTERVAL 10
|
||||
|
||||
#define IPV4LL_NETWORK 0xA9FE0000L
|
||||
#define IPV4LL_NETMASK 0xFFFF0000L
|
||||
|
||||
typedef enum IPv4LLTrigger{
|
||||
IPV4LL_TRIGGER_NULL,
|
||||
IPV4LL_TRIGGER_PACKET,
|
||||
IPV4LL_TRIGGER_TIMEOUT,
|
||||
_IPV4LL_TRIGGER_MAX,
|
||||
_IPV4LL_TRIGGER_INVALID = -1
|
||||
} IPv4LLTrigger;
|
||||
|
||||
typedef enum IPv4LLState {
|
||||
IPV4LL_STATE_INIT,
|
||||
IPV4LL_STATE_WAITING_PROBE,
|
||||
IPV4LL_STATE_PROBING,
|
||||
IPV4LL_STATE_WAITING_ANNOUNCE,
|
||||
IPV4LL_STATE_ANNOUNCING,
|
||||
IPV4LL_STATE_RUNNING,
|
||||
IPV4LL_STATE_STOPPED,
|
||||
_IPV4LL_STATE_MAX,
|
||||
_IPV4LL_STATE_INVALID = -1
|
||||
} IPv4LLState;
|
||||
|
||||
struct sd_ipv4ll {
|
||||
RefCount n_ref;
|
||||
|
||||
IPv4LLState state;
|
||||
int index;
|
||||
int fd;
|
||||
union sockaddr_union link;
|
||||
int iteration;
|
||||
int conflict;
|
||||
sd_event_source *receive_message;
|
||||
sd_event_source *timer;
|
||||
usec_t next_wakeup;
|
||||
usec_t defend_window;
|
||||
int next_wakeup_valid;
|
||||
be32_t address;
|
||||
struct random_data *random_data;
|
||||
char *random_data_state;
|
||||
/* External */
|
||||
be32_t claimed_address;
|
||||
struct ether_addr mac_addr;
|
||||
sd_event *event;
|
||||
int event_priority;
|
||||
sd_ipv4ll_cb_t cb;
|
||||
void* userdata;
|
||||
};
|
||||
|
||||
static void ipv4ll_run_state_machine(sd_ipv4ll *ll, IPv4LLTrigger trigger, void *trigger_data);
|
||||
|
||||
static void ipv4ll_set_state(sd_ipv4ll *ll, IPv4LLState st, int reset_counter) {
|
||||
|
||||
assert(ll);
|
||||
assert(st < _IPV4LL_STATE_MAX);
|
||||
|
||||
if (st == ll->state && !reset_counter) {
|
||||
ll->iteration++;
|
||||
} else {
|
||||
ll->state = st;
|
||||
ll->iteration = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static sd_ipv4ll *ipv4ll_client_notify(sd_ipv4ll *ll, int event) {
|
||||
assert(ll);
|
||||
|
||||
if (ll->cb) {
|
||||
ll = sd_ipv4ll_ref(ll);
|
||||
ll->cb(ll, event, ll->userdata);
|
||||
ll = sd_ipv4ll_unref(ll);
|
||||
}
|
||||
|
||||
return ll;
|
||||
}
|
||||
|
||||
static sd_ipv4ll *ipv4ll_stop(sd_ipv4ll *ll, int event) {
|
||||
assert(ll);
|
||||
|
||||
ll->receive_message = sd_event_source_unref(ll->receive_message);
|
||||
ll->fd = safe_close(ll->fd);
|
||||
|
||||
ll->timer = sd_event_source_unref(ll->timer);
|
||||
|
||||
log_ipv4ll(ll, "STOPPED");
|
||||
|
||||
ll = ipv4ll_client_notify(ll, event);
|
||||
|
||||
if (ll) {
|
||||
ll->claimed_address = 0;
|
||||
ipv4ll_set_state (ll, IPV4LL_STATE_INIT, 1);
|
||||
}
|
||||
|
||||
return ll;
|
||||
}
|
||||
|
||||
static int ipv4ll_pick_address(sd_ipv4ll *ll, be32_t *address) {
|
||||
be32_t addr;
|
||||
int r;
|
||||
int32_t random;
|
||||
|
||||
assert(ll);
|
||||
assert(address);
|
||||
assert(ll->random_data);
|
||||
|
||||
do {
|
||||
r = random_r(ll->random_data, &random);
|
||||
if (r < 0)
|
||||
return r;
|
||||
addr = htonl((random & 0x0000FFFF) | IPV4LL_NETWORK);
|
||||
} while (addr == ll->address ||
|
||||
(ntohl(addr) & IPV4LL_NETMASK) != IPV4LL_NETWORK ||
|
||||
(ntohl(addr) & 0x0000FF00) == 0x0000 ||
|
||||
(ntohl(addr) & 0x0000FF00) == 0xFF00);
|
||||
|
||||
*address = addr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ipv4ll_timer(sd_event_source *s, uint64_t usec, void *userdata) {
|
||||
sd_ipv4ll *ll = (sd_ipv4ll*)userdata;
|
||||
|
||||
assert(ll);
|
||||
|
||||
ll->next_wakeup_valid = 0;
|
||||
ipv4ll_run_state_machine(ll, IPV4LL_TRIGGER_TIMEOUT, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ipv4ll_set_next_wakeup(sd_ipv4ll *ll, int sec, int random_sec) {
|
||||
usec_t next_timeout = 0;
|
||||
usec_t time_now = 0;
|
||||
|
||||
assert(sec >= 0);
|
||||
assert(random_sec >= 0);
|
||||
assert(ll);
|
||||
|
||||
next_timeout = sec * USEC_PER_SEC;
|
||||
|
||||
if (random_sec)
|
||||
next_timeout += random_u32() % (random_sec * USEC_PER_SEC);
|
||||
|
||||
if (sd_event_now(ll->event, clock_boottime_or_monotonic(), &time_now) < 0)
|
||||
time_now = now(clock_boottime_or_monotonic());
|
||||
|
||||
ll->next_wakeup = time_now + next_timeout;
|
||||
ll->next_wakeup_valid = 1;
|
||||
}
|
||||
|
||||
static bool ipv4ll_arp_conflict (sd_ipv4ll *ll, struct ether_arp *arp) {
|
||||
assert(ll);
|
||||
assert(arp);
|
||||
|
||||
if (memcmp(arp->arp_spa, &ll->address, sizeof(ll->address)) == 0 &&
|
||||
memcmp(arp->arp_sha, &ll->mac_addr, ETH_ALEN) != 0)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool ipv4ll_arp_probe_conflict (sd_ipv4ll *ll, struct ether_arp *arp) {
|
||||
assert(ll);
|
||||
assert(arp);
|
||||
|
||||
if (ipv4ll_arp_conflict(ll, arp))
|
||||
return true;
|
||||
|
||||
if (memcmp(arp->arp_tpa, &ll->address, sizeof(ll->address)) == 0 &&
|
||||
memcmp(arp->arp_sha, &ll->mac_addr, ETH_ALEN))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void ipv4ll_run_state_machine(sd_ipv4ll *ll, IPv4LLTrigger trigger, void *trigger_data) {
|
||||
struct ether_arp out_packet;
|
||||
int out_packet_ready = 0;
|
||||
int r = 0;
|
||||
|
||||
assert(ll);
|
||||
assert(trigger < _IPV4LL_TRIGGER_MAX);
|
||||
|
||||
if (ll->state == IPV4LL_STATE_INIT) {
|
||||
|
||||
log_ipv4ll(ll, "PROBE");
|
||||
ipv4ll_set_state(ll, IPV4LL_STATE_WAITING_PROBE, 1);
|
||||
ipv4ll_set_next_wakeup(ll, 0, PROBE_WAIT);
|
||||
|
||||
} else if ((ll->state == IPV4LL_STATE_WAITING_PROBE && trigger == IPV4LL_TRIGGER_TIMEOUT) ||
|
||||
(ll->state == IPV4LL_STATE_PROBING && trigger == IPV4LL_TRIGGER_TIMEOUT && ll->iteration < PROBE_NUM-2)) {
|
||||
|
||||
/* Send a probe */
|
||||
arp_packet_probe(&out_packet, ll->address, &ll->mac_addr);
|
||||
out_packet_ready = 1;
|
||||
ipv4ll_set_state(ll, IPV4LL_STATE_PROBING, 0);
|
||||
|
||||
ipv4ll_set_next_wakeup(ll, PROBE_MIN, (PROBE_MAX-PROBE_MIN));
|
||||
|
||||
} else if (ll->state == IPV4LL_STATE_PROBING && trigger == IPV4LL_TRIGGER_TIMEOUT && ll->iteration >= PROBE_NUM-2) {
|
||||
|
||||
/* Send the last probe */
|
||||
arp_packet_probe(&out_packet, ll->address, &ll->mac_addr);
|
||||
out_packet_ready = 1;
|
||||
ipv4ll_set_state(ll, IPV4LL_STATE_WAITING_ANNOUNCE, 1);
|
||||
|
||||
ipv4ll_set_next_wakeup(ll, ANNOUNCE_WAIT, 0);
|
||||
|
||||
} else if ((ll->state == IPV4LL_STATE_WAITING_ANNOUNCE && trigger == IPV4LL_TRIGGER_TIMEOUT) ||
|
||||
(ll->state == IPV4LL_STATE_ANNOUNCING && trigger == IPV4LL_TRIGGER_TIMEOUT && ll->iteration < ANNOUNCE_NUM-1)) {
|
||||
|
||||
/* Send announcement packet */
|
||||
arp_packet_announcement(&out_packet, ll->address, &ll->mac_addr);
|
||||
out_packet_ready = 1;
|
||||
ipv4ll_set_state(ll, IPV4LL_STATE_ANNOUNCING, 0);
|
||||
|
||||
ipv4ll_set_next_wakeup(ll, ANNOUNCE_INTERVAL, 0);
|
||||
|
||||
if (ll->iteration == 0) {
|
||||
log_ipv4ll(ll, "ANNOUNCE");
|
||||
ll->claimed_address = ll->address;
|
||||
ll = ipv4ll_client_notify(ll, IPV4LL_EVENT_BIND);
|
||||
if (!ll || ll->state == IPV4LL_STATE_STOPPED)
|
||||
goto out;
|
||||
|
||||
ll->conflict = 0;
|
||||
}
|
||||
|
||||
} else if ((ll->state == IPV4LL_STATE_ANNOUNCING && trigger == IPV4LL_TRIGGER_TIMEOUT &&
|
||||
ll->iteration >= ANNOUNCE_NUM-1)) {
|
||||
|
||||
ipv4ll_set_state(ll, IPV4LL_STATE_RUNNING, 0);
|
||||
ll->next_wakeup_valid = 0;
|
||||
|
||||
} else if (trigger == IPV4LL_TRIGGER_PACKET) {
|
||||
|
||||
int conflicted = 0;
|
||||
usec_t time_now;
|
||||
struct ether_arp* in_packet = (struct ether_arp*)trigger_data;
|
||||
|
||||
assert(in_packet);
|
||||
|
||||
if (IN_SET(ll->state, IPV4LL_STATE_ANNOUNCING, IPV4LL_STATE_RUNNING)) {
|
||||
|
||||
if (ipv4ll_arp_conflict(ll, in_packet)) {
|
||||
|
||||
r = sd_event_now(ll->event, clock_boottime_or_monotonic(), &time_now);
|
||||
if (r < 0)
|
||||
goto out;
|
||||
|
||||
/* Defend address */
|
||||
if (time_now > ll->defend_window) {
|
||||
ll->defend_window = time_now + DEFEND_INTERVAL * USEC_PER_SEC;
|
||||
arp_packet_announcement(&out_packet, ll->address, &ll->mac_addr);
|
||||
out_packet_ready = 1;
|
||||
} else
|
||||
conflicted = 1;
|
||||
}
|
||||
|
||||
} else if (IN_SET(ll->state, IPV4LL_STATE_WAITING_PROBE,
|
||||
IPV4LL_STATE_PROBING,
|
||||
IPV4LL_STATE_WAITING_ANNOUNCE)) {
|
||||
|
||||
conflicted = ipv4ll_arp_probe_conflict(ll, in_packet);
|
||||
}
|
||||
|
||||
if (conflicted) {
|
||||
log_ipv4ll(ll, "CONFLICT");
|
||||
ll = ipv4ll_client_notify(ll, IPV4LL_EVENT_CONFLICT);
|
||||
if (!ll || ll->state == IPV4LL_STATE_STOPPED)
|
||||
goto out;
|
||||
|
||||
ll->claimed_address = 0;
|
||||
|
||||
/* Pick a new address */
|
||||
r = ipv4ll_pick_address(ll, &ll->address);
|
||||
if (r < 0)
|
||||
goto out;
|
||||
ll->conflict++;
|
||||
ll->defend_window = 0;
|
||||
ipv4ll_set_state(ll, IPV4LL_STATE_WAITING_PROBE, 1);
|
||||
|
||||
if (ll->conflict >= MAX_CONFLICTS) {
|
||||
log_ipv4ll(ll, "MAX_CONFLICTS");
|
||||
ipv4ll_set_next_wakeup(ll, RATE_LIMIT_INTERVAL, PROBE_WAIT);
|
||||
} else
|
||||
ipv4ll_set_next_wakeup(ll, 0, PROBE_WAIT);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (out_packet_ready) {
|
||||
r = arp_network_send_raw_socket(ll->fd, &ll->link, &out_packet);
|
||||
if (r < 0) {
|
||||
log_ipv4ll(ll, "failed to send arp packet out");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (ll->next_wakeup_valid) {
|
||||
ll->timer = sd_event_source_unref(ll->timer);
|
||||
r = sd_event_add_time(ll->event, &ll->timer, clock_boottime_or_monotonic(),
|
||||
ll->next_wakeup, 0, ipv4ll_timer, ll);
|
||||
if (r < 0)
|
||||
goto out;
|
||||
|
||||
r = sd_event_source_set_priority(ll->timer, ll->event_priority);
|
||||
if (r < 0)
|
||||
goto out;
|
||||
|
||||
r = sd_event_source_set_description(ll->timer, "ipv4ll-timer");
|
||||
if (r < 0)
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
if (r < 0 && ll)
|
||||
ipv4ll_stop(ll, r);
|
||||
}
|
||||
|
||||
static int ipv4ll_receive_message(sd_event_source *s, int fd,
|
||||
uint32_t revents, void *userdata) {
|
||||
int r;
|
||||
struct ether_arp arp;
|
||||
sd_ipv4ll *ll = (sd_ipv4ll*)userdata;
|
||||
|
||||
assert(ll);
|
||||
|
||||
r = read(fd, &arp, sizeof(struct ether_arp));
|
||||
if (r < (int) sizeof(struct ether_arp))
|
||||
return 0;
|
||||
|
||||
r = arp_packet_verify_headers(&arp);
|
||||
if (r < 0)
|
||||
return 0;
|
||||
|
||||
ipv4ll_run_state_machine(ll, IPV4LL_TRIGGER_PACKET, &arp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_ipv4ll_set_index(sd_ipv4ll *ll, int interface_index) {
|
||||
assert_return(ll, -EINVAL);
|
||||
assert_return(interface_index > 0, -EINVAL);
|
||||
assert_return(IN_SET(ll->state, IPV4LL_STATE_INIT,
|
||||
IPV4LL_STATE_STOPPED), -EBUSY);
|
||||
|
||||
ll->index = interface_index;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_ipv4ll_set_mac(sd_ipv4ll *ll, const struct ether_addr *addr) {
|
||||
bool need_restart = false;
|
||||
|
||||
assert_return(ll, -EINVAL);
|
||||
assert_return(addr, -EINVAL);
|
||||
|
||||
if (memcmp(&ll->mac_addr, addr, ETH_ALEN) == 0)
|
||||
return 0;
|
||||
|
||||
if (!IN_SET(ll->state, IPV4LL_STATE_INIT, IPV4LL_STATE_STOPPED)) {
|
||||
log_ipv4ll(ll, "Changing MAC address on running IPv4LL "
|
||||
"client, restarting");
|
||||
ll = ipv4ll_stop(ll, IPV4LL_EVENT_STOP);
|
||||
need_restart = true;
|
||||
}
|
||||
|
||||
if (!ll)
|
||||
return 0;
|
||||
|
||||
memcpy(&ll->mac_addr, addr, ETH_ALEN);
|
||||
|
||||
if (need_restart)
|
||||
sd_ipv4ll_start(ll);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_ipv4ll_detach_event(sd_ipv4ll *ll) {
|
||||
assert_return(ll, -EINVAL);
|
||||
|
||||
ll->event = sd_event_unref(ll->event);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_ipv4ll_attach_event(sd_ipv4ll *ll, sd_event *event, int priority) {
|
||||
int r;
|
||||
|
||||
assert_return(ll, -EINVAL);
|
||||
assert_return(!ll->event, -EBUSY);
|
||||
|
||||
if (event)
|
||||
ll->event = sd_event_ref(event);
|
||||
else {
|
||||
r = sd_event_default(&ll->event);
|
||||
if (r < 0) {
|
||||
ipv4ll_stop(ll, IPV4LL_EVENT_STOP);
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
ll->event_priority = priority;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_ipv4ll_set_callback(sd_ipv4ll *ll, sd_ipv4ll_cb_t cb, void *userdata) {
|
||||
assert_return(ll, -EINVAL);
|
||||
|
||||
ll->cb = cb;
|
||||
ll->userdata = userdata;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_ipv4ll_get_address(sd_ipv4ll *ll, struct in_addr *address){
|
||||
assert_return(ll, -EINVAL);
|
||||
assert_return(address, -EINVAL);
|
||||
|
||||
if (ll->claimed_address == 0) {
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
address->s_addr = ll->claimed_address;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_ipv4ll_set_address_seed (sd_ipv4ll *ll, uint8_t seed[8]) {
|
||||
unsigned int entropy;
|
||||
int r;
|
||||
|
||||
assert_return(ll, -EINVAL);
|
||||
assert_return(seed, -EINVAL);
|
||||
|
||||
entropy = *seed;
|
||||
|
||||
free(ll->random_data);
|
||||
free(ll->random_data_state);
|
||||
|
||||
ll->random_data = new0(struct random_data, 1);
|
||||
ll->random_data_state = new0(char, 128);
|
||||
|
||||
if (!ll->random_data || !ll->random_data_state) {
|
||||
r = -ENOMEM;
|
||||
goto error;
|
||||
}
|
||||
|
||||
r = initstate_r((unsigned int)entropy, ll->random_data_state, 128, ll->random_data);
|
||||
if (r < 0)
|
||||
goto error;
|
||||
|
||||
error:
|
||||
if (r < 0){
|
||||
free(ll->random_data);
|
||||
free(ll->random_data_state);
|
||||
ll->random_data = NULL;
|
||||
ll->random_data_state = NULL;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
bool sd_ipv4ll_is_running(sd_ipv4ll *ll) {
|
||||
assert_return(ll, -EINVAL);
|
||||
|
||||
return !IN_SET(ll->state, IPV4LL_STATE_INIT, IPV4LL_STATE_STOPPED);
|
||||
}
|
||||
|
||||
#define HASH_KEY SD_ID128_MAKE(df,04,22,98,3f,ad,14,52,f9,87,2e,d1,9c,70,e2,f2)
|
||||
|
||||
int sd_ipv4ll_start (sd_ipv4ll *ll) {
|
||||
int r;
|
||||
|
||||
assert_return(ll, -EINVAL);
|
||||
assert_return(ll->event, -EINVAL);
|
||||
assert_return(ll->index > 0, -EINVAL);
|
||||
assert_return(IN_SET(ll->state, IPV4LL_STATE_INIT,
|
||||
IPV4LL_STATE_STOPPED), -EBUSY);
|
||||
|
||||
ll->state = IPV4LL_STATE_INIT;
|
||||
|
||||
r = arp_network_bind_raw_socket(ll->index, &ll->link);
|
||||
|
||||
if (r < 0)
|
||||
goto out;
|
||||
|
||||
ll->fd = r;
|
||||
ll->conflict = 0;
|
||||
ll->defend_window = 0;
|
||||
ll->claimed_address = 0;
|
||||
|
||||
if (!ll->random_data) {
|
||||
uint8_t seed[8];
|
||||
|
||||
/* Fallback to mac */
|
||||
siphash24(seed, &ll->mac_addr.ether_addr_octet,
|
||||
ETH_ALEN, HASH_KEY.bytes);
|
||||
|
||||
r = sd_ipv4ll_set_address_seed(ll, seed);
|
||||
if (r < 0)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (ll->address == 0) {
|
||||
r = ipv4ll_pick_address(ll, &ll->address);
|
||||
if (r < 0)
|
||||
goto out;
|
||||
}
|
||||
|
||||
ipv4ll_set_state (ll, IPV4LL_STATE_INIT, 1);
|
||||
|
||||
r = sd_event_add_io(ll->event, &ll->receive_message, ll->fd,
|
||||
EPOLLIN, ipv4ll_receive_message, ll);
|
||||
if (r < 0)
|
||||
goto out;
|
||||
|
||||
r = sd_event_source_set_priority(ll->receive_message, ll->event_priority);
|
||||
if (r < 0)
|
||||
goto out;
|
||||
|
||||
r = sd_event_source_set_description(ll->receive_message, "ipv4ll-receive-message");
|
||||
if (r < 0)
|
||||
goto out;
|
||||
|
||||
r = sd_event_add_time(ll->event,
|
||||
&ll->timer,
|
||||
clock_boottime_or_monotonic(),
|
||||
now(clock_boottime_or_monotonic()), 0,
|
||||
ipv4ll_timer, ll);
|
||||
|
||||
if (r < 0)
|
||||
goto out;
|
||||
|
||||
r = sd_event_source_set_priority(ll->timer, ll->event_priority);
|
||||
if (r < 0)
|
||||
goto out;
|
||||
|
||||
r = sd_event_source_set_description(ll->timer, "ipv4ll-timer");
|
||||
out:
|
||||
if (r < 0)
|
||||
ipv4ll_stop(ll, IPV4LL_EVENT_STOP);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_ipv4ll_stop(sd_ipv4ll *ll) {
|
||||
ipv4ll_stop(ll, IPV4LL_EVENT_STOP);
|
||||
if (ll)
|
||||
ipv4ll_set_state(ll, IPV4LL_STATE_STOPPED, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
sd_ipv4ll *sd_ipv4ll_ref(sd_ipv4ll *ll) {
|
||||
if (ll)
|
||||
assert_se(REFCNT_INC(ll->n_ref) >= 2);
|
||||
|
||||
return ll;
|
||||
}
|
||||
|
||||
sd_ipv4ll *sd_ipv4ll_unref(sd_ipv4ll *ll) {
|
||||
if (ll && REFCNT_DEC(ll->n_ref) == 0) {
|
||||
ll->receive_message =
|
||||
sd_event_source_unref(ll->receive_message);
|
||||
ll->fd = safe_close(ll->fd);
|
||||
|
||||
ll->timer = sd_event_source_unref(ll->timer);
|
||||
|
||||
sd_ipv4ll_detach_event(ll);
|
||||
|
||||
free(ll->random_data);
|
||||
free(ll->random_data_state);
|
||||
free(ll);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return ll;
|
||||
}
|
||||
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(sd_ipv4ll*, sd_ipv4ll_unref);
|
||||
#define _cleanup_ipv4ll_free_ _cleanup_(sd_ipv4ll_unrefp)
|
||||
|
||||
int sd_ipv4ll_new(sd_ipv4ll **ret) {
|
||||
_cleanup_ipv4ll_free_ sd_ipv4ll *ll = NULL;
|
||||
|
||||
assert_return(ret, -EINVAL);
|
||||
|
||||
ll = new0(sd_ipv4ll, 1);
|
||||
if (!ll)
|
||||
return -ENOMEM;
|
||||
|
||||
ll->n_ref = REFCNT_INIT;
|
||||
ll->state = IPV4LL_STATE_INIT;
|
||||
ll->index = -1;
|
||||
ll->fd = -1;
|
||||
|
||||
*ret = ll;
|
||||
ll = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
@@ -470,30 +470,19 @@ char* path_join(const char *root, const char *path, const char *rest) {
|
||||
NULL);
|
||||
}
|
||||
|
||||
int path_is_mount_point(const char *t, bool allow_symlink) {
|
||||
|
||||
union file_handle_union h = FILE_HANDLE_INIT;
|
||||
int fd_is_mount_point(int fd) {
|
||||
union file_handle_union h = FILE_HANDLE_INIT, h_parent = FILE_HANDLE_INIT;
|
||||
int mount_id = -1, mount_id_parent = -1;
|
||||
bool nosupp = false;
|
||||
struct stat a, b;
|
||||
int r;
|
||||
_cleanup_close_ int fd = -1;
|
||||
bool nosupp = false;
|
||||
|
||||
assert(fd >= 0);
|
||||
|
||||
/* We are not actually interested in the file handles, but
|
||||
* name_to_handle_at() also passes us the mount ID, hence use
|
||||
* it but throw the handle away */
|
||||
|
||||
if (path_equal(t, "/"))
|
||||
return 1;
|
||||
|
||||
fd = openat(AT_FDCWD, t, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|(allow_symlink ? 0 : O_PATH));
|
||||
if (fd < 0) {
|
||||
if (errno == ENOENT)
|
||||
return 0;
|
||||
|
||||
return -errno;
|
||||
}
|
||||
|
||||
r = name_to_handle_at(fd, "", &h.handle, &mount_id, AT_EMPTY_PATH);
|
||||
if (r < 0) {
|
||||
if (errno == ENOSYS)
|
||||
@@ -502,53 +491,81 @@ int path_is_mount_point(const char *t, bool allow_symlink) {
|
||||
goto fallback;
|
||||
else if (errno == EOPNOTSUPP)
|
||||
/* This kernel or file system does not support
|
||||
* name_to_handle_at(), hence fallback to the
|
||||
* name_to_handle_at(), hence let's see if the
|
||||
* upper fs supports it (in which case it is a
|
||||
* mount point), otherwise fallback to the
|
||||
* traditional stat() logic */
|
||||
nosupp = true;
|
||||
else if (errno == ENOENT)
|
||||
return 0;
|
||||
else
|
||||
return -errno;
|
||||
}
|
||||
|
||||
|
||||
h.handle.handle_bytes = MAX_HANDLE_SZ;
|
||||
r = name_to_handle_at(fd, "..", &h.handle, &mount_id_parent, 0);
|
||||
if (r < 0)
|
||||
if (errno == EOPNOTSUPP)
|
||||
r = name_to_handle_at(fd, "..", &h_parent.handle, &mount_id_parent, 0);
|
||||
if (r < 0) {
|
||||
if (errno == EOPNOTSUPP) {
|
||||
if (nosupp)
|
||||
/* Neither parent nor child do name_to_handle_at()?
|
||||
We have no choice but to fall back. */
|
||||
goto fallback;
|
||||
else
|
||||
/* The parent can't do name_to_handle_at() but
|
||||
* the directory we are interested in can?
|
||||
* Or the other way around?
|
||||
/* The parent can't do name_to_handle_at() but the
|
||||
* directory we are interested in can?
|
||||
* If so, it must be a mount point. */
|
||||
return 1;
|
||||
else
|
||||
} else
|
||||
return -errno;
|
||||
else
|
||||
} else if (nosupp)
|
||||
/* The parent can do name_to_handle_at() but the
|
||||
* directory we are interested in can't? If so, it
|
||||
* must be a mount point. */
|
||||
return 1;
|
||||
else {
|
||||
/* If the file handle for the directory we are
|
||||
* interested in and its parent are identical, we
|
||||
* assume this is the root directory, which is a mount
|
||||
* point. */
|
||||
|
||||
if (h.handle.handle_bytes == h_parent.handle.handle_bytes &&
|
||||
h.handle.handle_type == h_parent.handle.handle_type &&
|
||||
memcmp(h.handle.f_handle, h_parent.handle.f_handle, h.handle.handle_bytes) == 0)
|
||||
return 1;
|
||||
|
||||
return mount_id != mount_id_parent;
|
||||
}
|
||||
|
||||
fallback:
|
||||
r = fstatat(fd, "", &a, AT_EMPTY_PATH);
|
||||
|
||||
if (r < 0) {
|
||||
if (errno == ENOENT)
|
||||
return 0;
|
||||
|
||||
if (r < 0)
|
||||
return -errno;
|
||||
}
|
||||
|
||||
|
||||
r = fstatat(fd, "..", &b, 0);
|
||||
if (r < 0)
|
||||
return -errno;
|
||||
|
||||
/* A directory with same device and inode as its parent? Must
|
||||
* be the root directory */
|
||||
if (a.st_dev == b.st_dev &&
|
||||
a.st_ino == b.st_ino)
|
||||
return 1;
|
||||
|
||||
return a.st_dev != b.st_dev;
|
||||
}
|
||||
|
||||
int path_is_mount_point(const char *t, bool allow_symlink) {
|
||||
_cleanup_close_ int fd = -1;
|
||||
|
||||
assert(t);
|
||||
|
||||
if (path_equal(t, "/"))
|
||||
return 1;
|
||||
|
||||
fd = openat(AT_FDCWD, t, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|(allow_symlink ? 0 : O_PATH));
|
||||
if (fd < 0)
|
||||
return -errno;
|
||||
|
||||
return fd_is_mount_point(fd);
|
||||
}
|
||||
|
||||
int path_is_read_only_fs(const char *path) {
|
||||
struct statvfs st;
|
||||
|
||||
|
@@ -53,6 +53,7 @@ char** path_strv_make_absolute_cwd(char **l);
|
||||
char** path_strv_resolve(char **l, const char *prefix);
|
||||
char** path_strv_resolve_uniq(char **l, const char *prefix);
|
||||
|
||||
int fd_is_mount_point(int fd);
|
||||
int path_is_mount_point(const char *path, bool allow_symlink);
|
||||
int path_is_read_only_fs(const char *path);
|
||||
int path_is_os_tree(const char *path);
|
||||
|
@@ -278,7 +278,7 @@ char **strv_split_newlines(const char *s) {
|
||||
return l;
|
||||
}
|
||||
|
||||
int strv_split_quoted(char ***t, const char *s, bool relax) {
|
||||
int strv_split_quoted(char ***t, const char *s, UnquoteFlags flags) {
|
||||
size_t n = 0, allocated = 0;
|
||||
_cleanup_strv_free_ char **l = NULL;
|
||||
int r;
|
||||
@@ -289,7 +289,7 @@ int strv_split_quoted(char ***t, const char *s, bool relax) {
|
||||
for (;;) {
|
||||
_cleanup_free_ char *word = NULL;
|
||||
|
||||
r = unquote_first_word(&s, &word, relax);
|
||||
r = unquote_first_word(&s, &word, flags);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
|
@@ -73,7 +73,7 @@ static inline bool strv_isempty(char * const *l) {
|
||||
char **strv_split(const char *s, const char *separator);
|
||||
char **strv_split_newlines(const char *s);
|
||||
|
||||
int strv_split_quoted(char ***t, const char *s, bool relax);
|
||||
int strv_split_quoted(char ***t, const char *s, UnquoteFlags flags);
|
||||
|
||||
char *strv_join(char **l, const char *separator);
|
||||
char *strv_join_quoted(char **l);
|
||||
|
@@ -25,7 +25,6 @@
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
#include <libintl.h>
|
||||
#include <locale.h>
|
||||
#include <stdio.h>
|
||||
#include <syslog.h>
|
||||
#include <sched.h>
|
||||
@@ -1347,12 +1346,132 @@ char *cescape(const char *s) {
|
||||
return r;
|
||||
}
|
||||
|
||||
char *cunescape_length_with_prefix(const char *s, size_t length, const char *prefix) {
|
||||
static int cunescape_one(const char *p, size_t length, char *ret) {
|
||||
int r = 1;
|
||||
|
||||
assert(p);
|
||||
assert(*p);
|
||||
assert(ret);
|
||||
|
||||
if (length != (size_t) -1 && length < 1)
|
||||
return -EINVAL;
|
||||
|
||||
switch (p[0]) {
|
||||
|
||||
case 'a':
|
||||
*ret = '\a';
|
||||
break;
|
||||
case 'b':
|
||||
*ret = '\b';
|
||||
break;
|
||||
case 'f':
|
||||
*ret = '\f';
|
||||
break;
|
||||
case 'n':
|
||||
*ret = '\n';
|
||||
break;
|
||||
case 'r':
|
||||
*ret = '\r';
|
||||
break;
|
||||
case 't':
|
||||
*ret = '\t';
|
||||
break;
|
||||
case 'v':
|
||||
*ret = '\v';
|
||||
break;
|
||||
case '\\':
|
||||
*ret = '\\';
|
||||
break;
|
||||
case '"':
|
||||
*ret = '"';
|
||||
break;
|
||||
case '\'':
|
||||
*ret = '\'';
|
||||
break;
|
||||
|
||||
case 's':
|
||||
/* This is an extension of the XDG syntax files */
|
||||
*ret = ' ';
|
||||
break;
|
||||
|
||||
case 'x': {
|
||||
/* hexadecimal encoding */
|
||||
int a, b;
|
||||
|
||||
if (length != (size_t) -1 && length < 3)
|
||||
return -EINVAL;
|
||||
|
||||
a = unhexchar(p[1]);
|
||||
if (a < 0)
|
||||
return -EINVAL;
|
||||
|
||||
b = unhexchar(p[2]);
|
||||
if (b < 0)
|
||||
return -EINVAL;
|
||||
|
||||
/* don't allow NUL bytes */
|
||||
if (a == 0 && b == 0)
|
||||
return -EINVAL;
|
||||
|
||||
*ret = (char) ((a << 4) | b);
|
||||
r = 3;
|
||||
break;
|
||||
}
|
||||
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7': {
|
||||
/* octal encoding */
|
||||
int a, b, c, m;
|
||||
|
||||
if (length != (size_t) -1 && length < 4)
|
||||
return -EINVAL;
|
||||
|
||||
a = unoctchar(p[0]);
|
||||
if (a < 0)
|
||||
return -EINVAL;
|
||||
|
||||
b = unoctchar(p[1]);
|
||||
if (b < 0)
|
||||
return -EINVAL;
|
||||
|
||||
c = unoctchar(p[2]);
|
||||
if (c < 0)
|
||||
return -EINVAL;
|
||||
|
||||
/* don't allow NUL bytes */
|
||||
if (a == 0 && b == 0 && c == 0)
|
||||
return -EINVAL;
|
||||
|
||||
/* Don't allow bytes above 255 */
|
||||
m = (a << 6) | (b << 3) | c;
|
||||
if (m > 255)
|
||||
return -EINVAL;
|
||||
|
||||
*ret = (char) m;
|
||||
r = 3;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int cunescape_length_with_prefix(const char *s, size_t length, const char *prefix, UnescapeFlags flags, char **ret) {
|
||||
char *r, *t;
|
||||
const char *f;
|
||||
size_t pl;
|
||||
|
||||
assert(s);
|
||||
assert(ret);
|
||||
|
||||
/* Undoes C style string escaping, and optionally prefixes it. */
|
||||
|
||||
@@ -1360,135 +1479,61 @@ char *cunescape_length_with_prefix(const char *s, size_t length, const char *pre
|
||||
|
||||
r = new(char, pl+length+1);
|
||||
if (!r)
|
||||
return NULL;
|
||||
return -ENOMEM;
|
||||
|
||||
if (prefix)
|
||||
memcpy(r, prefix, pl);
|
||||
|
||||
for (f = s, t = r + pl; f < s + length; f++) {
|
||||
size_t remaining = s + length - f;
|
||||
size_t remaining;
|
||||
int k;
|
||||
|
||||
remaining = s + length - f;
|
||||
assert(remaining > 0);
|
||||
|
||||
if (*f != '\\') { /* a literal literal */
|
||||
if (*f != '\\') {
|
||||
/* A literal literal, copy verbatim */
|
||||
*(t++) = *f;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (--remaining == 0) { /* copy trailing backslash verbatim */
|
||||
*(t++) = *f;
|
||||
break;
|
||||
}
|
||||
|
||||
f++;
|
||||
|
||||
switch (*f) {
|
||||
|
||||
case 'a':
|
||||
*(t++) = '\a';
|
||||
break;
|
||||
case 'b':
|
||||
*(t++) = '\b';
|
||||
break;
|
||||
case 'f':
|
||||
*(t++) = '\f';
|
||||
break;
|
||||
case 'n':
|
||||
*(t++) = '\n';
|
||||
break;
|
||||
case 'r':
|
||||
*(t++) = '\r';
|
||||
break;
|
||||
case 't':
|
||||
*(t++) = '\t';
|
||||
break;
|
||||
case 'v':
|
||||
*(t++) = '\v';
|
||||
break;
|
||||
case '\\':
|
||||
*(t++) = '\\';
|
||||
break;
|
||||
case '"':
|
||||
*(t++) = '"';
|
||||
break;
|
||||
case '\'':
|
||||
*(t++) = '\'';
|
||||
break;
|
||||
|
||||
case 's':
|
||||
/* This is an extension of the XDG syntax files */
|
||||
*(t++) = ' ';
|
||||
break;
|
||||
|
||||
case 'x': {
|
||||
/* hexadecimal encoding */
|
||||
int a = -1, b = -1;
|
||||
|
||||
if (remaining >= 2) {
|
||||
a = unhexchar(f[1]);
|
||||
b = unhexchar(f[2]);
|
||||
if (remaining == 1) {
|
||||
if (flags & UNESCAPE_RELAX) {
|
||||
/* A trailing backslash, copy verbatim */
|
||||
*(t++) = *f;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (a < 0 || b < 0 || (a == 0 && b == 0)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
k = cunescape_one(f + 1, remaining - 1, t);
|
||||
if (k < 0) {
|
||||
if (flags & UNESCAPE_RELAX) {
|
||||
/* Invalid escape code, let's take it literal then */
|
||||
*(t++) = '\\';
|
||||
*(t++) = 'x';
|
||||
} else {
|
||||
*(t++) = (char) ((a << 4) | b);
|
||||
f += 2;
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
return k;
|
||||
}
|
||||
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7': {
|
||||
/* octal encoding */
|
||||
int a = -1, b = -1, c = -1;
|
||||
|
||||
if (remaining >= 3) {
|
||||
a = unoctchar(f[0]);
|
||||
b = unoctchar(f[1]);
|
||||
c = unoctchar(f[2]);
|
||||
}
|
||||
|
||||
if (a < 0 || b < 0 || c < 0 || (a == 0 && b == 0 && c == 0)) {
|
||||
/* Invalid escape code, let's take it literal then */
|
||||
*(t++) = '\\';
|
||||
*(t++) = f[0];
|
||||
} else {
|
||||
*(t++) = (char) ((a << 6) | (b << 3) | c);
|
||||
f += 2;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
/* Invalid escape code, let's take it literal then */
|
||||
*(t++) = '\\';
|
||||
*(t++) = *f;
|
||||
break;
|
||||
}
|
||||
f += k;
|
||||
t++;
|
||||
}
|
||||
|
||||
*t = 0;
|
||||
return r;
|
||||
|
||||
*ret = r;
|
||||
return t - r;
|
||||
}
|
||||
|
||||
char *cunescape_length(const char *s, size_t length) {
|
||||
return cunescape_length_with_prefix(s, length, NULL);
|
||||
int cunescape_length(const char *s, size_t length, UnescapeFlags flags, char **ret) {
|
||||
return cunescape_length_with_prefix(s, length, NULL, flags, ret);
|
||||
}
|
||||
|
||||
char *cunescape(const char *s) {
|
||||
assert(s);
|
||||
|
||||
return cunescape_length(s, strlen(s));
|
||||
int cunescape(const char *s, UnescapeFlags flags, char **ret) {
|
||||
return cunescape_length(s, strlen(s), flags, ret);
|
||||
}
|
||||
|
||||
char *xescape(const char *s, const char *bad) {
|
||||
@@ -2851,7 +2896,7 @@ int getttyname_malloc(int fd, char **ret) {
|
||||
|
||||
int getttyname_harder(int fd, char **r) {
|
||||
int k;
|
||||
char *s;
|
||||
char *s = NULL;
|
||||
|
||||
k = getttyname_malloc(fd, &s);
|
||||
if (k < 0)
|
||||
@@ -2957,101 +3002,14 @@ int get_ctty(pid_t pid, dev_t *_devnr, char **r) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rm_rf_children_dangerous(int fd, bool only_dirs, bool honour_sticky, struct stat *root_dev) {
|
||||
_cleanup_closedir_ DIR *d = NULL;
|
||||
int ret = 0;
|
||||
|
||||
assert(fd >= 0);
|
||||
|
||||
/* This returns the first error we run into, but nevertheless
|
||||
* tries to go on. This closes the passed fd. */
|
||||
|
||||
d = fdopendir(fd);
|
||||
if (!d) {
|
||||
safe_close(fd);
|
||||
|
||||
return errno == ENOENT ? 0 : -errno;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
struct dirent *de;
|
||||
bool is_dir, keep_around;
|
||||
struct stat st;
|
||||
int r;
|
||||
|
||||
errno = 0;
|
||||
de = readdir(d);
|
||||
if (!de) {
|
||||
if (errno != 0 && ret == 0)
|
||||
ret = -errno;
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (streq(de->d_name, ".") || streq(de->d_name, ".."))
|
||||
continue;
|
||||
|
||||
if (de->d_type == DT_UNKNOWN ||
|
||||
honour_sticky ||
|
||||
(de->d_type == DT_DIR && root_dev)) {
|
||||
if (fstatat(fd, de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
|
||||
if (ret == 0 && errno != ENOENT)
|
||||
ret = -errno;
|
||||
continue;
|
||||
}
|
||||
|
||||
is_dir = S_ISDIR(st.st_mode);
|
||||
keep_around =
|
||||
honour_sticky &&
|
||||
(st.st_uid == 0 || st.st_uid == getuid()) &&
|
||||
(st.st_mode & S_ISVTX);
|
||||
} else {
|
||||
is_dir = de->d_type == DT_DIR;
|
||||
keep_around = false;
|
||||
}
|
||||
|
||||
if (is_dir) {
|
||||
int subdir_fd;
|
||||
|
||||
/* if root_dev is set, remove subdirectories only, if device is same as dir */
|
||||
if (root_dev && st.st_dev != root_dev->st_dev)
|
||||
continue;
|
||||
|
||||
subdir_fd = openat(fd, de->d_name,
|
||||
O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
|
||||
if (subdir_fd < 0) {
|
||||
if (ret == 0 && errno != ENOENT)
|
||||
ret = -errno;
|
||||
continue;
|
||||
}
|
||||
|
||||
r = rm_rf_children_dangerous(subdir_fd, only_dirs, honour_sticky, root_dev);
|
||||
if (r < 0 && ret == 0)
|
||||
ret = r;
|
||||
|
||||
if (!keep_around)
|
||||
if (unlinkat(fd, de->d_name, AT_REMOVEDIR) < 0) {
|
||||
if (ret == 0 && errno != ENOENT)
|
||||
ret = -errno;
|
||||
}
|
||||
|
||||
} else if (!only_dirs && !keep_around) {
|
||||
|
||||
if (unlinkat(fd, de->d_name, 0) < 0) {
|
||||
if (ret == 0 && errno != ENOENT)
|
||||
ret = -errno;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_pure_ static int is_temporary_fs(struct statfs *s) {
|
||||
bool is_temporary_fs(const struct statfs *s) {
|
||||
assert(s);
|
||||
|
||||
return F_TYPE_EQUAL(s->f_type, TMPFS_MAGIC) ||
|
||||
F_TYPE_EQUAL(s->f_type, RAMFS_MAGIC);
|
||||
}
|
||||
|
||||
int is_fd_on_temporary_fs(int fd) {
|
||||
int fd_is_temporary_fs(int fd) {
|
||||
struct statfs s;
|
||||
|
||||
if (fstatfs(fd, &s) < 0)
|
||||
@@ -3060,114 +3018,6 @@ int is_fd_on_temporary_fs(int fd) {
|
||||
return is_temporary_fs(&s);
|
||||
}
|
||||
|
||||
int rm_rf_children(int fd, bool only_dirs, bool honour_sticky, struct stat *root_dev) {
|
||||
struct statfs s;
|
||||
|
||||
assert(fd >= 0);
|
||||
|
||||
if (fstatfs(fd, &s) < 0) {
|
||||
safe_close(fd);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
/* We refuse to clean disk file systems with this call. This
|
||||
* is extra paranoia just to be sure we never ever remove
|
||||
* non-state data */
|
||||
if (!is_temporary_fs(&s)) {
|
||||
log_error("Attempted to remove disk file system, and we can't allow that.");
|
||||
safe_close(fd);
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
return rm_rf_children_dangerous(fd, only_dirs, honour_sticky, root_dev);
|
||||
}
|
||||
|
||||
static int file_is_priv_sticky(const char *p) {
|
||||
struct stat st;
|
||||
|
||||
assert(p);
|
||||
|
||||
if (lstat(p, &st) < 0)
|
||||
return -errno;
|
||||
|
||||
return
|
||||
(st.st_uid == 0 || st.st_uid == getuid()) &&
|
||||
(st.st_mode & S_ISVTX);
|
||||
}
|
||||
|
||||
static int rm_rf_internal(const char *path, bool only_dirs, bool delete_root, bool honour_sticky, bool dangerous) {
|
||||
int fd, r;
|
||||
struct statfs s;
|
||||
|
||||
assert(path);
|
||||
|
||||
/* We refuse to clean the root file system with this
|
||||
* call. This is extra paranoia to never cause a really
|
||||
* seriously broken system. */
|
||||
if (path_equal(path, "/")) {
|
||||
log_error("Attempted to remove entire root file system, and we can't allow that.");
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
fd = open(path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
|
||||
if (fd < 0) {
|
||||
|
||||
if (errno != ENOTDIR && errno != ELOOP)
|
||||
return -errno;
|
||||
|
||||
if (!dangerous) {
|
||||
if (statfs(path, &s) < 0)
|
||||
return -errno;
|
||||
|
||||
if (!is_temporary_fs(&s)) {
|
||||
log_error("Attempted to remove disk file system, and we can't allow that.");
|
||||
return -EPERM;
|
||||
}
|
||||
}
|
||||
|
||||
if (delete_root && !only_dirs)
|
||||
if (unlink(path) < 0 && errno != ENOENT)
|
||||
return -errno;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!dangerous) {
|
||||
if (fstatfs(fd, &s) < 0) {
|
||||
safe_close(fd);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
if (!is_temporary_fs(&s)) {
|
||||
log_error("Attempted to remove disk file system, and we can't allow that.");
|
||||
safe_close(fd);
|
||||
return -EPERM;
|
||||
}
|
||||
}
|
||||
|
||||
r = rm_rf_children_dangerous(fd, only_dirs, honour_sticky, NULL);
|
||||
if (delete_root) {
|
||||
|
||||
if (honour_sticky && file_is_priv_sticky(path) > 0)
|
||||
return r;
|
||||
|
||||
if (rmdir(path) < 0 && errno != ENOENT) {
|
||||
if (r == 0)
|
||||
r = -errno;
|
||||
}
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int rm_rf(const char *path, bool only_dirs, bool delete_root, bool honour_sticky) {
|
||||
return rm_rf_internal(path, only_dirs, delete_root, honour_sticky, false);
|
||||
}
|
||||
|
||||
int rm_rf_dangerous(const char *path, bool only_dirs, bool delete_root, bool honour_sticky) {
|
||||
return rm_rf_internal(path, only_dirs, delete_root, honour_sticky, true);
|
||||
}
|
||||
|
||||
int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) {
|
||||
assert(path);
|
||||
|
||||
@@ -3404,14 +3254,14 @@ char **replace_env_argv(char **argv, char **env) {
|
||||
/* If $FOO appears as single word, replace it by the split up variable */
|
||||
if ((*i)[0] == '$' && (*i)[1] != '{') {
|
||||
char *e;
|
||||
char **w, **m;
|
||||
char **w, **m = NULL;
|
||||
unsigned q;
|
||||
|
||||
e = strv_env_get(env, *i+1);
|
||||
if (e) {
|
||||
int r;
|
||||
|
||||
r = strv_split_quoted(&m, e, true);
|
||||
r = strv_split_quoted(&m, e, UNQUOTE_RELAX);
|
||||
if (r < 0) {
|
||||
ret[k] = NULL;
|
||||
strv_free(ret);
|
||||
@@ -6382,7 +6232,7 @@ int parse_proc_cmdline(int (*parse_item)(const char *key, const char *value)) {
|
||||
_cleanup_free_ char *word = NULL;
|
||||
char *value = NULL;
|
||||
|
||||
r = unquote_first_word(&p, &word, true);
|
||||
r = unquote_first_word(&p, &word, UNQUOTE_RELAX);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
@@ -6422,7 +6272,7 @@ int get_proc_cmdline_key(const char *key, char **value) {
|
||||
_cleanup_free_ char *word = NULL;
|
||||
const char *e;
|
||||
|
||||
r = unquote_first_word(&p, &word, true);
|
||||
r = unquote_first_word(&p, &word, UNQUOTE_RELAX);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
@@ -6895,9 +6745,9 @@ int umount_recursive(const char *prefix, int flags) {
|
||||
continue;
|
||||
}
|
||||
|
||||
p = cunescape(path);
|
||||
if (!p)
|
||||
return -ENOMEM;
|
||||
r = cunescape(path, UNESCAPE_RELAX, &p);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!path_startswith(p, prefix))
|
||||
continue;
|
||||
@@ -6997,9 +6847,9 @@ int bind_remount_recursive(const char *prefix, bool ro) {
|
||||
continue;
|
||||
}
|
||||
|
||||
p = cunescape(path);
|
||||
if (!p)
|
||||
return -ENOMEM;
|
||||
r = cunescape(path, UNESCAPE_RELAX, &p);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Let's ignore autofs mounts. If they aren't
|
||||
* triggered yet, we want to avoid triggering
|
||||
@@ -7275,9 +7125,10 @@ int is_dir(const char* path, bool follow) {
|
||||
return !!S_ISDIR(st.st_mode);
|
||||
}
|
||||
|
||||
int unquote_first_word(const char **p, char **ret, bool relax) {
|
||||
int unquote_first_word(const char **p, char **ret, UnquoteFlags flags) {
|
||||
_cleanup_free_ char *s = NULL;
|
||||
size_t allocated = 0, sz = 0;
|
||||
int r;
|
||||
|
||||
enum {
|
||||
START,
|
||||
@@ -7335,7 +7186,7 @@ int unquote_first_word(const char **p, char **ret, bool relax) {
|
||||
|
||||
case VALUE_ESCAPE:
|
||||
if (c == 0) {
|
||||
if (relax)
|
||||
if (flags & UNQUOTE_RELAX)
|
||||
goto finish;
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -7343,6 +7194,14 @@ int unquote_first_word(const char **p, char **ret, bool relax) {
|
||||
if (!GREEDY_REALLOC(s, allocated, sz+2))
|
||||
return -ENOMEM;
|
||||
|
||||
if (flags & UNQUOTE_CUNESCAPE) {
|
||||
r = cunescape_one(*p, (size_t) -1, &c);
|
||||
if (r < 0)
|
||||
return -EINVAL;
|
||||
|
||||
(*p) += r - 1;
|
||||
}
|
||||
|
||||
s[sz++] = c;
|
||||
state = VALUE;
|
||||
|
||||
@@ -7350,7 +7209,7 @@ int unquote_first_word(const char **p, char **ret, bool relax) {
|
||||
|
||||
case SINGLE_QUOTE:
|
||||
if (c == 0) {
|
||||
if (relax)
|
||||
if (flags & UNQUOTE_RELAX)
|
||||
goto finish;
|
||||
return -EINVAL;
|
||||
} else if (c == '\'')
|
||||
@@ -7368,7 +7227,7 @@ int unquote_first_word(const char **p, char **ret, bool relax) {
|
||||
|
||||
case SINGLE_QUOTE_ESCAPE:
|
||||
if (c == 0) {
|
||||
if (relax)
|
||||
if (flags & UNQUOTE_RELAX)
|
||||
goto finish;
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -7376,6 +7235,14 @@ int unquote_first_word(const char **p, char **ret, bool relax) {
|
||||
if (!GREEDY_REALLOC(s, allocated, sz+2))
|
||||
return -ENOMEM;
|
||||
|
||||
if (flags & UNQUOTE_CUNESCAPE) {
|
||||
r = cunescape_one(*p, (size_t) -1, &c);
|
||||
if (r < 0)
|
||||
return -EINVAL;
|
||||
|
||||
(*p) += r - 1;
|
||||
}
|
||||
|
||||
s[sz++] = c;
|
||||
state = SINGLE_QUOTE;
|
||||
break;
|
||||
@@ -7398,7 +7265,7 @@ int unquote_first_word(const char **p, char **ret, bool relax) {
|
||||
|
||||
case DOUBLE_QUOTE_ESCAPE:
|
||||
if (c == 0) {
|
||||
if (relax)
|
||||
if (flags & UNQUOTE_RELAX)
|
||||
goto finish;
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -7406,6 +7273,14 @@ int unquote_first_word(const char **p, char **ret, bool relax) {
|
||||
if (!GREEDY_REALLOC(s, allocated, sz+2))
|
||||
return -ENOMEM;
|
||||
|
||||
if (flags & UNQUOTE_CUNESCAPE) {
|
||||
r = cunescape_one(*p, (size_t) -1, &c);
|
||||
if (r < 0)
|
||||
return -EINVAL;
|
||||
|
||||
(*p) += r - 1;
|
||||
}
|
||||
|
||||
s[sz++] = c;
|
||||
state = DOUBLE_QUOTE;
|
||||
break;
|
||||
@@ -7435,7 +7310,7 @@ finish:
|
||||
return 1;
|
||||
}
|
||||
|
||||
int unquote_many_words(const char **p, ...) {
|
||||
int unquote_many_words(const char **p, UnquoteFlags flags, ...) {
|
||||
va_list ap;
|
||||
char **l;
|
||||
int n = 0, i, c, r;
|
||||
@@ -7446,7 +7321,7 @@ int unquote_many_words(const char **p, ...) {
|
||||
assert(p);
|
||||
|
||||
/* Count how many words are expected */
|
||||
va_start(ap, p);
|
||||
va_start(ap, flags);
|
||||
for (;;) {
|
||||
if (!va_arg(ap, char **))
|
||||
break;
|
||||
@@ -7461,7 +7336,7 @@ int unquote_many_words(const char **p, ...) {
|
||||
l = newa0(char*, n);
|
||||
for (c = 0; c < n; c++) {
|
||||
|
||||
r = unquote_first_word(p, &l[c], false);
|
||||
r = unquote_first_word(p, &l[c], flags);
|
||||
if (r < 0) {
|
||||
int j;
|
||||
|
||||
@@ -7477,7 +7352,7 @@ int unquote_many_words(const char **p, ...) {
|
||||
|
||||
/* If we managed to parse all words, return them in the passed
|
||||
* in parameters */
|
||||
va_start(ap, p);
|
||||
va_start(ap, flags);
|
||||
for (i = 0; i < n; i++) {
|
||||
char **v;
|
||||
|
||||
|
@@ -41,6 +41,7 @@
|
||||
#include <locale.h>
|
||||
#include <mntent.h>
|
||||
#include <sys/inotify.h>
|
||||
#include <sys/statfs.h>
|
||||
|
||||
#if SIZEOF_PID_T == 4
|
||||
# define PID_PRI PRIi32
|
||||
@@ -310,9 +311,14 @@ char decchar(int x) _const_;
|
||||
int undecchar(char c) _const_;
|
||||
|
||||
char *cescape(const char *s);
|
||||
char *cunescape(const char *s);
|
||||
char *cunescape_length(const char *s, size_t length);
|
||||
char *cunescape_length_with_prefix(const char *s, size_t length, const char *prefix);
|
||||
|
||||
typedef enum UnescapeFlags {
|
||||
UNESCAPE_RELAX = 1,
|
||||
} UnescapeFlags;
|
||||
|
||||
int cunescape(const char *s, UnescapeFlags flags, char **ret);
|
||||
int cunescape_length(const char *s, size_t length, UnescapeFlags flags, char **ret);
|
||||
int cunescape_length_with_prefix(const char *s, size_t length, const char *prefix, UnescapeFlags flags, char **ret);
|
||||
|
||||
char *xescape(const char *s, const char *bad);
|
||||
|
||||
@@ -461,12 +467,8 @@ int get_ctty(pid_t, dev_t *_devnr, char **r);
|
||||
int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid);
|
||||
int fchmod_and_fchown(int fd, mode_t mode, uid_t uid, gid_t gid);
|
||||
|
||||
int is_fd_on_temporary_fs(int fd);
|
||||
|
||||
int rm_rf_children(int fd, bool only_dirs, bool honour_sticky, struct stat *root_dev);
|
||||
int rm_rf_children_dangerous(int fd, bool only_dirs, bool honour_sticky, struct stat *root_dev);
|
||||
int rm_rf(const char *path, bool only_dirs, bool delete_root, bool honour_sticky);
|
||||
int rm_rf_dangerous(const char *path, bool only_dirs, bool delete_root, bool honour_sticky);
|
||||
bool is_temporary_fs(const struct statfs *s) _pure_;
|
||||
int fd_is_temporary_fs(int fd);
|
||||
|
||||
int pipe_eof(int fd);
|
||||
|
||||
@@ -1017,8 +1019,13 @@ int take_password_lock(const char *root);
|
||||
int is_symlink(const char *path);
|
||||
int is_dir(const char *path, bool follow);
|
||||
|
||||
int unquote_first_word(const char **p, char **ret, bool relax);
|
||||
int unquote_many_words(const char **p, ...) _sentinel_;
|
||||
typedef enum UnquoteFlags {
|
||||
UNQUOTE_RELAX = 1,
|
||||
UNQUOTE_CUNESCAPE = 2,
|
||||
} UnquoteFlags;
|
||||
|
||||
int unquote_first_word(const char **p, char **ret, UnquoteFlags flags);
|
||||
int unquote_many_words(const char **p, UnquoteFlags flags, ...) _sentinel_;
|
||||
|
||||
int free_and_strdup(char **p, const char *s);
|
||||
|
||||
|
54
src/systemd/src/systemd/sd-ipv4ll.h
Normal file
54
src/systemd/src/systemd/sd-ipv4ll.h
Normal file
@@ -0,0 +1,54 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
|
||||
|
||||
#ifndef foosdipv4llfoo
|
||||
#define foosdipv4llfoo
|
||||
|
||||
/***
|
||||
This file is part of systemd.
|
||||
|
||||
Copyright (C) 2014 Axis Communications AB. All rights reserved.
|
||||
|
||||
systemd is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
systemd is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <netinet/in.h>
|
||||
#include <net/ethernet.h>
|
||||
|
||||
#include "sd-event.h"
|
||||
|
||||
enum {
|
||||
IPV4LL_EVENT_STOP = 0,
|
||||
IPV4LL_EVENT_BIND = 1,
|
||||
IPV4LL_EVENT_CONFLICT = 2,
|
||||
};
|
||||
|
||||
typedef struct sd_ipv4ll sd_ipv4ll;
|
||||
typedef void (*sd_ipv4ll_cb_t)(sd_ipv4ll *ll, int event, void *userdata);
|
||||
|
||||
int sd_ipv4ll_detach_event(sd_ipv4ll *ll);
|
||||
int sd_ipv4ll_attach_event(sd_ipv4ll *ll, sd_event *event, int priority);
|
||||
int sd_ipv4ll_get_address(sd_ipv4ll *ll, struct in_addr *address);
|
||||
int sd_ipv4ll_set_callback(sd_ipv4ll *ll, sd_ipv4ll_cb_t cb, void *userdata);
|
||||
int sd_ipv4ll_set_mac(sd_ipv4ll *ll, const struct ether_addr *addr);
|
||||
int sd_ipv4ll_set_index(sd_ipv4ll *ll, int interface_index);
|
||||
int sd_ipv4ll_set_address_seed(sd_ipv4ll *ll, uint8_t seed[8]);
|
||||
bool sd_ipv4ll_is_running(sd_ipv4ll *ll);
|
||||
int sd_ipv4ll_start(sd_ipv4ll *ll);
|
||||
int sd_ipv4ll_stop(sd_ipv4ll *ll);
|
||||
sd_ipv4ll *sd_ipv4ll_ref(sd_ipv4ll *ll);
|
||||
sd_ipv4ll *sd_ipv4ll_unref(sd_ipv4ll *ll);
|
||||
int sd_ipv4ll_new (sd_ipv4ll **ret);
|
||||
|
||||
#endif
|
Reference in New Issue
Block a user