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:
Beniamino Galvani
2015-04-08 11:15:46 +02:00
parent 8af18182f3
commit eab389bd8a
11 changed files with 1174 additions and 372 deletions

View 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__)

View 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;
}

View 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;
}

View 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;
}

View File

@@ -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;

View File

@@ -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);

View File

@@ -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)

View File

@@ -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);

View File

@@ -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;

View File

@@ -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);

View 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