Commit 5507dd68 authored by Thomas Haller's avatar Thomas Haller

systemd: update code from upstream

This is a direct dump from systemd git on 2016-08-11, git commit
9c5077fed42dc3cd2517a7ab816babef549dd079.

======

SYSTEMD_DIR=../systemd
COMMIT=9c5077fed42dc3cd2517a7ab816babef549dd079

(
  cd "$SYSTEMD_DIR"
  git checkout "$COMMIT"
  git reset --hard
  git clean -fdx
)

git ls-files :/src/systemd/src/ | xargs -d '\n' rm -f

nm_copy_sd() {
    mkdir -p "./src/systemd/$(dirname "$1")"
    cp "$SYSTEMD_DIR/$1" "./src/systemd/$1"
}

nm_copy_sd "src/basic/alloc-util.c"
nm_copy_sd "src/basic/alloc-util.h"
nm_copy_sd "src/basic/async.h"
nm_copy_sd "src/basic/escape.c"
nm_copy_sd "src/basic/escape.h"
nm_copy_sd "src/basic/ether-addr-util.c"
nm_copy_sd "src/basic/ether-addr-util.h"
nm_copy_sd "src/basic/extract-word.c"
nm_copy_sd "src/basic/extract-word.h"
nm_copy_sd "src/basic/fileio.c"
nm_copy_sd "src/basic/fileio.h"
nm_copy_sd "src/basic/fd-util.c"
nm_copy_sd "src/basic/fd-util.h"
nm_copy_sd "src/basic/fs-util.c"
nm_copy_sd "src/basic/fs-util.h"
nm_copy_sd "src/basic/hash-funcs.c"
nm_copy_sd "src/basic/hash-funcs.h"
nm_copy_sd "src/basic/hashmap.c"
nm_copy_sd "src/basic/hashmap.h"
nm_copy_sd "src/basic/hexdecoct.c"
nm_copy_sd "src/basic/hexdecoct.h"
nm_copy_sd "src/basic/hostname-util.c"
nm_copy_sd "src/basic/hostname-util.h"
nm_copy_sd "src/basic/in-addr-util.c"
nm_copy_sd "src/basic/in-addr-util.h"
nm_copy_sd "src/basic/io-util.c"
nm_copy_sd "src/basic/io-util.h"
nm_copy_sd "src/basic/list.h"
nm_copy_sd "src/basic/log.h"
nm_copy_sd "src/basic/macro.h"
nm_copy_sd "src/basic/mempool.h"
nm_copy_sd "src/basic/mempool.c"
nm_copy_sd "src/basic/parse-util.c"
nm_copy_sd "src/basic/parse-util.h"
nm_copy_sd "src/basic/path-util.c"
nm_copy_sd "src/basic/path-util.h"
nm_copy_sd "src/basic/prioq.h"
nm_copy_sd "src/basic/prioq.c"
nm_copy_sd "src/basic/random-util.c"
nm_copy_sd "src/basic/random-util.h"
nm_copy_sd "src/basic/refcnt.h"
nm_copy_sd "src/basic/set.h"
nm_copy_sd "src/basic/signal-util.h"
nm_copy_sd "src/basic/siphash24.c"
nm_copy_sd "src/basic/siphash24.h"
nm_copy_sd "src/basic/socket-util.c"
nm_copy_sd "src/basic/socket-util.h"
nm_copy_sd "src/basic/sparse-endian.h"
nm_copy_sd "src/basic/stdio-util.h"
nm_copy_sd "src/basic/string-table.c"
nm_copy_sd "src/basic/string-table.h"
nm_copy_sd "src/basic/string-util.c"
nm_copy_sd "src/basic/string-util.h"
nm_copy_sd "src/basic/strv.c"
nm_copy_sd "src/basic/strv.h"
nm_copy_sd "src/basic/time-util.c"
nm_copy_sd "src/basic/time-util.h"
nm_copy_sd "src/basic/umask-util.h"
nm_copy_sd "src/basic/unaligned.h"
nm_copy_sd "src/basic/utf8.c"
nm_copy_sd "src/basic/utf8.h"
nm_copy_sd "src/basic/util.c"
nm_copy_sd "src/basic/util.h"
nm_copy_sd "src/libsystemd-network/arp-util.c"
nm_copy_sd "src/libsystemd-network/arp-util.h"
nm_copy_sd "src/libsystemd-network/dhcp6-internal.h"
nm_copy_sd "src/libsystemd-network/dhcp6-lease-internal.h"
nm_copy_sd "src/libsystemd-network/dhcp6-network.c"
nm_copy_sd "src/libsystemd-network/dhcp6-option.c"
nm_copy_sd "src/libsystemd-network/dhcp6-protocol.h"
nm_copy_sd "src/libsystemd-network/dhcp-identifier.c"
nm_copy_sd "src/libsystemd-network/dhcp-identifier.h"
nm_copy_sd "src/libsystemd-network/dhcp-internal.h"
nm_copy_sd "src/libsystemd-network/dhcp-lease-internal.h"
nm_copy_sd "src/libsystemd-network/dhcp-network.c"
nm_copy_sd "src/libsystemd-network/dhcp-option.c"
nm_copy_sd "src/libsystemd-network/dhcp-packet.c"
nm_copy_sd "src/libsystemd-network/dhcp-protocol.h"
nm_copy_sd "src/libsystemd-network/lldp-internal.h"
nm_copy_sd "src/libsystemd-network/lldp-neighbor.c"
nm_copy_sd "src/libsystemd-network/lldp-neighbor.h"
nm_copy_sd "src/libsystemd-network/lldp-network.c"
nm_copy_sd "src/libsystemd-network/lldp-network.h"
nm_copy_sd "src/libsystemd-network/network-internal.c"
nm_copy_sd "src/libsystemd-network/network-internal.h"
nm_copy_sd "src/libsystemd-network/sd-dhcp6-client.c"
nm_copy_sd "src/libsystemd-network/sd-dhcp6-lease.c"
nm_copy_sd "src/libsystemd-network/sd-dhcp-client.c"
nm_copy_sd "src/libsystemd-network/sd-dhcp-lease.c"
nm_copy_sd "src/libsystemd-network/sd-ipv4ll.c"
nm_copy_sd "src/libsystemd-network/sd-ipv4acd.c"
nm_copy_sd "src/libsystemd-network/sd-lldp.c"
nm_copy_sd "src/libsystemd/sd-event/sd-event.c"
nm_copy_sd "src/libsystemd/sd-id128/id128-util.c"
nm_copy_sd "src/libsystemd/sd-id128/id128-util.h"
nm_copy_sd "src/libsystemd/sd-id128/sd-id128.c"
nm_copy_sd "src/shared/dns-domain.c"
nm_copy_sd "src/shared/dns-domain.h"
nm_copy_sd "src/systemd/_sd-common.h"
nm_copy_sd "src/systemd/sd-dhcp6-client.h"
nm_copy_sd "src/systemd/sd-dhcp6-lease.h"
nm_copy_sd "src/systemd/sd-dhcp-client.h"
nm_copy_sd "src/systemd/sd-dhcp-lease.h"
nm_copy_sd "src/systemd/sd-event.h"
nm_copy_sd "src/systemd/sd-ndisc.h"
nm_copy_sd "src/systemd/sd-id128.h"
nm_copy_sd "src/systemd/sd-ipv4acd.h"
nm_copy_sd "src/systemd/sd-ipv4ll.h"
nm_copy_sd "src/systemd/sd-lldp.h"
parent 21e3aa91
......@@ -186,6 +186,12 @@ int fd_cloexec(int fd, bool cloexec) {
return 0;
}
void stdio_unset_cloexec(void) {
fd_cloexec(STDIN_FILENO, false);
fd_cloexec(STDOUT_FILENO, false);
fd_cloexec(STDERR_FILENO, false);
}
_pure_ static bool fd_in_set(int fd, const int fdset[], unsigned n_fdset) {
unsigned i;
......
......@@ -63,6 +63,7 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(DIR*, closedir);
int fd_nonblock(int fd, bool nonblock);
int fd_cloexec(int fd, bool cloexec);
void stdio_unset_cloexec(void);
int close_all_fds(const int except[], unsigned n_except);
......
......@@ -47,6 +47,8 @@
#include "umask-util.h"
#include "utf8.h"
#define READ_FULL_BYTES_MAX (4U*1024U*1024U)
int write_string_stream(FILE *f, const char *line, bool enforce_newline) {
assert(f);
......@@ -230,7 +232,7 @@ int read_full_stream(FILE *f, char **contents, size_t *size) {
if (S_ISREG(st.st_mode)) {
/* Safety check */
if (st.st_size > 4*1024*1024)
if (st.st_size > READ_FULL_BYTES_MAX)
return -E2BIG;
/* Start with the right file size, but be prepared for
......@@ -245,26 +247,31 @@ int read_full_stream(FILE *f, char **contents, size_t *size) {
char *t;
size_t k;
t = realloc(buf, n+1);
t = realloc(buf, n + 1);
if (!t)
return -ENOMEM;
buf = t;
k = fread(buf + l, 1, n - l, f);
if (k > 0)
l += k;
if (k <= 0) {
if (ferror(f))
return -errno;
if (ferror(f))
return -errno;
if (feof(f))
break;
}
l += k;
n *= 2;
/* We aren't expecting fread() to return a short read outside
* of (error && eof), assert buffer is full and enlarge buffer.
*/
assert(l == n);
/* Safety check */
if (n > 4*1024*1024)
if (n >= READ_FULL_BYTES_MAX)
return -E2BIG;
n = MIN(n * 2, READ_FULL_BYTES_MAX);
}
buf[l] = 0;
......@@ -1067,7 +1074,7 @@ int fflush_and_check(FILE *f) {
return 0;
}
/* This is much like like mkostemp() but is subject to umask(). */
/* This is much like mkostemp() but is subject to umask(). */
int mkostemp_safe(char *pattern, int flags) {
_cleanup_umask_ mode_t u = 0;
int fd;
......@@ -1161,8 +1168,8 @@ int tempfn_random_child(const char *p, const char *extra, char **ret) {
char *t, *x;
uint64_t u;
unsigned i;
int r;
assert(p);
assert(ret);
/* Turns this:
......@@ -1171,6 +1178,12 @@ int tempfn_random_child(const char *p, const char *extra, char **ret) {
* /foo/bar/waldo/.#<extra>3c2b6219aa75d7d0
*/
if (!p) {
r = tmp_dir(&p);
if (r < 0)
return r;
}
if (!extra)
extra = "";
......@@ -1257,9 +1270,13 @@ int fputs_with_space(FILE *f, const char *s, const char *separator, bool *space)
int open_tmpfile_unlinkable(const char *directory, int flags) {
char *p;
int fd;
int fd, r;
assert(directory);
if (!directory) {
r = tmp_dir(&directory);
if (r < 0)
return r;
}
/* Returns an unlinked temporary file that cannot be linked into the file system anymore */
......@@ -1354,3 +1371,44 @@ int link_tmpfile(int fd, const char *path, const char *target) {
return 0;
}
int read_nul_string(FILE *f, char **ret) {
_cleanup_free_ char *x = NULL;
size_t allocated = 0, n = 0;
assert(f);
assert(ret);
/* Reads a NUL-terminated string from the specified file. */
for (;;) {
int c;
if (!GREEDY_REALLOC(x, allocated, n+2))
return -ENOMEM;
c = fgetc(f);
if (c == 0) /* Terminate at NUL byte */
break;
if (c == EOF) {
if (ferror(f))
return -errno;
break; /* Terminate at EOF */
}
x[n++] = (char) c;
}
if (x)
x[n] = 0;
else {
x = new0(char, 1);
if (!x)
return -ENOMEM;
}
*ret = x;
x = NULL;
return 0;
}
......@@ -86,3 +86,5 @@ int open_tmpfile_unlinkable(const char *directory, int flags);
int open_tmpfile_linkable(const char *target, int flags, char **ret_path);
int link_tmpfile(int fd, const char *path, const char *target);
int read_nul_string(FILE *f, char **ret);
......@@ -38,6 +38,7 @@
#include "mkdir.h"
#include "parse-util.h"
#include "path-util.h"
#include "stat-util.h"
#include "stdio-util.h"
#include "string-util.h"
#include "strv.h"
......@@ -495,6 +496,94 @@ int get_files_in_directory(const char *path, char ***list) {
return n;
}
static int getenv_tmp_dir(const char **ret_path) {
const char *n;
int r, ret = 0;
assert(ret_path);
/* We use the same order of environment variables python uses in tempfile.gettempdir():
* https://docs.python.org/3/library/tempfile.html#tempfile.gettempdir */
FOREACH_STRING(n, "TMPDIR", "TEMP", "TMP") {
const char *e;
e = secure_getenv(n);
if (!e)
continue;
if (!path_is_absolute(e)) {
r = -ENOTDIR;
goto next;
}
if (!path_is_safe(e)) {
r = -EPERM;
goto next;
}
r = is_dir(e, true);
if (r < 0)
goto next;
if (r == 0) {
r = -ENOTDIR;
goto next;
}
*ret_path = e;
return 1;
next:
/* Remember first error, to make this more debuggable */
if (ret >= 0)
ret = r;
}
if (ret < 0)
return ret;
*ret_path = NULL;
return ret;
}
static int tmp_dir_internal(const char *def, const char **ret) {
const char *e;
int r, k;
assert(def);
assert(ret);
r = getenv_tmp_dir(&e);
if (r > 0) {
*ret = e;
return 0;
}
k = is_dir(def, true);
if (k == 0)
k = -ENOTDIR;
if (k < 0)
return r < 0 ? r : k;
*ret = def;
return 0;
}
int var_tmp_dir(const char **ret) {
/* Returns the location for "larger" temporary files, that is backed by physical storage if available, and thus
* even might survive a boot: /var/tmp. If $TMPDIR (or related environment variables) are set, its value is
* returned preferably however. Note that both this function and tmp_dir() below are affected by $TMPDIR,
* making it a variable that overrides all temporary file storage locations. */
return tmp_dir_internal("/var/tmp", ret);
}
int tmp_dir(const char **ret) {
/* Similar to var_tmp_dir() above, but returns the location for "smaller" temporary files, which is usually
* backed by an in-memory file system: /tmp. */
return tmp_dir_internal("/tmp", ret);
}
int inotify_add_watch_fd(int fd, int what, uint32_t mask) {
char path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1];
int r;
......
......@@ -61,6 +61,9 @@ int mkfifo_atomic(const char *path, mode_t mode);
int get_files_in_directory(const char *path, char ***list);
int tmp_dir(const char **ret);
int var_tmp_dir(const char **ret);
#define INOTIFY_EVENT_MAX (sizeof(struct inotify_event) + NAME_MAX + 1)
#define FOREACH_INOTIFY_EVENT(e, buffer, sz) \
......
......@@ -29,6 +29,7 @@
#include "extract-word.h"
#include "macro.h"
#include "parse-util.h"
#include "process-util.h"
#include "string-util.h"
int parse_boolean(const char *v) {
......@@ -533,7 +534,7 @@ int parse_fractional_part_u(const char **p, size_t digits, unsigned *res) {
return 0;
}
int parse_percent(const char *p) {
int parse_percent_unbounded(const char *p) {
const char *pc, *n;
unsigned v;
int r;
......@@ -546,8 +547,30 @@ int parse_percent(const char *p) {
r = safe_atou(n, &v);
if (r < 0)
return r;
return (int) v;
}
int parse_percent(const char *p) {
int v;
v = parse_percent_unbounded(p);
if (v > 100)
return -ERANGE;
return (int) v;
return v;
}
int parse_nice(const char *p, int *ret) {
int n, r;
r = safe_atoi(p, &n);
if (r < 0)
return r;
if (!nice_is_valid(n))
return -ERANGE;
*ret = n;
return 0;
}
......@@ -106,4 +106,7 @@ int safe_atod(const char *s, double *ret_d);
int parse_fractional_part_u(const char **s, size_t digits, unsigned *res);
int parse_percent_unbounded(const char *p);
int parse_percent(const char *p);
int parse_nice(const char *p, int *ret);
......@@ -23,8 +23,8 @@
#include "hashmap.h"
#include "macro.h"
Set *internal_set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
#define set_new(ops) internal_set_new(ops HASHMAP_DEBUG_SRC_ARGS)
Set *internal_set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
#define set_new(ops) internal_set_new(ops HASHMAP_DEBUG_SRC_ARGS)
static inline Set *set_free(Set *s) {
internal_hashmap_free(HASHMAP_BASE(s));
......@@ -42,8 +42,8 @@ static inline Set *set_copy(Set *s) {
return (Set*) internal_hashmap_copy(HASHMAP_BASE(s));
}
int internal_set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
#define set_ensure_allocated(h, ops) internal_set_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS)
int internal_set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
#define set_ensure_allocated(h, ops) internal_set_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS)
int set_put(Set *s, const void *key);
/* no set_update */
......
......@@ -1046,3 +1046,17 @@ int flush_accept(int fd) {
close(cfd);
}
}
struct cmsghdr* cmsg_find(struct msghdr *mh, int level, int type, socklen_t length) {
struct cmsghdr *cmsg;
assert(mh);
CMSG_FOREACH(cmsg, mh)
if (cmsg->cmsg_level == level &&
cmsg->cmsg_type == type &&
(length == (socklen_t) -1 || length == cmsg->cmsg_len))
return cmsg;
return NULL;
}
......@@ -142,6 +142,8 @@ int flush_accept(int fd);
#define CMSG_FOREACH(cmsg, mh) \
for ((cmsg) = CMSG_FIRSTHDR(mh); (cmsg); (cmsg) = CMSG_NXTHDR((mh), (cmsg)))
struct cmsghdr* cmsg_find(struct msghdr *mh, int level, int type, socklen_t length);
/* Covers only file system and abstract AF_UNIX socket addresses, but not unnamed socket addresses. */
#define SOCKADDR_UN_LEN(sa) \
({ \
......
......@@ -22,6 +22,7 @@
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "alloc-util.h"
#include "gunicode.h"
......@@ -323,6 +324,14 @@ char ascii_tolower(char x) {
return x;
}
char ascii_toupper(char x) {
if (x >= 'a' && x <= 'z')
return x - 'a' + 'A';
return x;
}
char *ascii_strlower(char *t) {
char *p;
......@@ -334,6 +343,17 @@ char *ascii_strlower(char *t) {
return t;
}
char *ascii_strupper(char *t) {
char *p;
assert(t);
for (p = t; *p; p++)
*p = ascii_toupper(*p);
return t;
}
char *ascii_strlower_n(char *t, size_t n) {
size_t i;
......@@ -803,25 +823,20 @@ int free_and_strdup(char **p, const char *s) {
return 1;
}
#pragma GCC push_options
#pragma GCC optimize("O0")
/*
* Pointer to memset is volatile so that compiler must de-reference
* the pointer and can't assume that it points to any function in
* particular (such as memset, which it then might further "optimize")
* This approach is inspired by openssl's crypto/mem_clr.c.
*/
typedef void *(*memset_t)(void *,int,size_t);
void* memory_erase(void *p, size_t l) {
volatile uint8_t* x = (volatile uint8_t*) p;
static volatile memset_t memset_func = memset;
/* This basically does what memset() does, but hopefully isn't
* optimized away by the compiler. One of those days, when
* glibc learns memset_s() we should replace this call by
* memset_s(), but until then this has to do. */
for (; l > 0; l--)
*(x++) = 'x';
return p;
void* memory_erase(void *p, size_t l) {
return memset_func(p, 'x', l);
}
#pragma GCC pop_options
char* string_erase(char *x) {
if (!x)
......
......@@ -137,6 +137,9 @@ char ascii_tolower(char x);
char *ascii_strlower(char *s);
char *ascii_strlower_n(char *s, size_t n);
char ascii_toupper(char x);
char *ascii_strupper(char *s);
int ascii_strcasecmp_n(const char *a, const char *b, size_t n);
int ascii_strcasecmp_nn(const char *a, size_t n, const char *b, size_t m);
......
......@@ -638,6 +638,17 @@ char **strv_remove(char **l, const char *s) {
}
char **strv_parse_nulstr(const char *s, size_t l) {
/* l is the length of the input data, which will be split at NULs into
* elements of the resulting strv. Hence, the number of items in the resulting strv
* will be equal to one plus the number of NUL bytes in the l bytes starting at s,
* unless s[l-1] is NUL, in which case the final empty string is not stored in
* the resulting strv, and length is equal to the number of NUL bytes.
*
* Note that contrary to a normal nulstr which cannot contain empty strings, because
* the input data is terminated by any two consequent NUL bytes, this parser accepts
* empty strings in s.
*/
const char *p;
unsigned c = 0, i = 0;
char **v;
......@@ -700,6 +711,13 @@ char **strv_split_nulstr(const char *s) {
}
int strv_make_nulstr(char **l, char **p, size_t *q) {
/* A valid nulstr with two NULs at the end will be created, but
* q will be the length without the two trailing NULs. Thus the output
* string is a valid nulstr and can be iterated over using NULSTR_FOREACH,
* and can also be parsed by strv_parse_nulstr as long as the length
* is provided separately.
*/
size_t n_allocated = 0, n = 0;
_cleanup_free_ char *m = NULL;
char **i;
......@@ -712,7 +730,7 @@ int strv_make_nulstr(char **l, char **p, size_t *q) {
z = strlen(*i);
if (!GREEDY_REALLOC(m, n_allocated, n + z + 1))
if (!GREEDY_REALLOC(m, n_allocated, n + z + 2))
return -ENOMEM;
memcpy(m + n, *i, z + 1);
......@@ -723,11 +741,14 @@ int strv_make_nulstr(char **l, char **p, size_t *q) {
m = new0(char, 1);
if (!m)
return -ENOMEM;
n = 0;
}
n = 1;
} else
/* make sure there is a second extra NUL at the end of resulting nulstr */
m[n] = '\0';
assert(n > 0);
*p = m;
*q = n;
*q = n - 1;
m = NULL;
......@@ -803,9 +824,8 @@ char **strv_reverse(char **l) {
if (n <= 1)
return l;
for (i = 0; i < n / 2; i++) {
for (i = 0; i < n / 2; i++)
SWAP_TWO(l[i], l[n-1-i]);
}
return l;
}
......@@ -876,7 +896,7 @@ int strv_extend_n(char ***l, const char *value, size_t n) {
if (n == 0)
return 0;
/* Adds the value value n times to l */
/* Adds the value n times to l */
k = strv_length(*l);
......
......@@ -254,32 +254,95 @@ struct timeval *timeval_store(struct timeval *tv, usec_t u) {
return tv;
}
static char *format_timestamp_internal(char *buf, size_t l, usec_t t,
bool utc, bool us) {
static char *format_timestamp_internal(
char *buf,
size_t l,
usec_t t,
bool utc,
bool us) {
/* The weekdays in non-localized (English) form. We use this instead of the localized form, so that our
* generated timestamps may be parsed with parse_timestamp(), and always read the same. */
static const char * const weekdays[] = {
[0] = "Sun",
[1] = "Mon",
[2] = "Tue",
[3] = "Wed",
[4] = "Thu",
[5] = "Fri",
[6] = "Sat",
};
struct tm tm;
time_t sec;
int k;
size_t n;
assert(buf);
assert(l > 0);
if (l <
3 + /* week day */
1 + 10 + /* space and date */
1 + 8 + /* space and time */
(us ? 1 + 6 : 0) + /* "." and microsecond part */
1 + 1 + /* space and shortest possible zone */
1)
return NULL; /* Not enough space even for the shortest form. */
if (t <= 0 || t == USEC_INFINITY)
return NULL; /* Timestamp is unset */
sec = (time_t) (t / USEC_PER_SEC); /* Round down */
if ((usec_t) sec != (t / USEC_PER_SEC))
return NULL; /* overflow? */
if (!localtime_or_gmtime_r(&sec, &tm, utc))
return NULL;
sec = (time_t) (t / USEC_PER_SEC);
localtime_or_gmtime_r(&sec, &tm, utc);
/* Start with the week day */
assert((size_t) tm.tm_wday < ELEMENTSOF(weekdays));
memcpy(buf, weekdays[tm.tm_wday], 4);
if (us)
k = strftime(buf, l, "%a %Y-%m-%d %H:%M:%S", &tm);
else
k = strftime(buf, l, "%a %Y-%m-%d %H:%M:%S %Z", &tm);
/* Add the main components */
if (strftime(buf + 3, l - 3, " %Y-%m-%d %H:%M:%S", &tm) <= 0)
return NULL; /* Doesn't fit */
if (k <= 0)
return NULL;
/* Append the microseconds part, if that's requested */
if (us) {
snprintf(buf + strlen(buf), l - strlen(buf), ".%06llu", (unsigned long long) (t % USEC_PER_SEC));
if (strftime(buf + strlen(buf), l - strlen(buf), " %Z", &tm) <= 0)
return NULL;
n = strlen(buf);
if (n + 8 > l)
return NULL; /* Microseconds part doesn't fit. */
sprintf(buf + n, ".%06llu", (unsigned long long) (t % USEC_PER_SEC));
}
/* Append the timezone */
n = strlen(buf);
if (utc) {
/* If this is UTC then let's explicitly use the "UTC" string here, because gmtime_r() normally uses the
* obsolete "GMT" instead. */
if (n + 5 > l)
return NULL; /* "UTC" doesn't fit. */
strcpy(buf + n, " UTC");
} else if (!isempty(tm.tm_zone)) {
size_t tn;
/* An explicit timezone is specified, let's use it, if it fits */
tn = strlen(tm.tm_zone);
if (n + 1 + tn + 1 > l) {
/* The full time zone does not fit in. Yuck. */
if (n + 1 + _POSIX_TZNAME_MAX + 1 > l)
return NULL; /* Not even enough space for the POSIX minimum (of 6)? In that case, complain that it doesn't fit */
/* So the time zone doesn't fit in fully, but the caller passed enough space for the POSIX
* minimum time zone length. In this case suppress the timezone entirely, in order not to dump
* an overly long, hard to read string on the user. This should be safe, because the user will
* assume the local timezone anyway if none is shown. And so does parse_timestamp(). */
} else {
buf[n++] = ' ';
strcpy(buf + n, tm.tm_zone);
}
}
return buf;
......@@ -539,12 +602,11 @@ int parse_timestamp(const char *t, usec_t *usec) {
{ "Sat", 6 },
};
const char *k;
const char *utc;
const char *k, *utc, *tzn = NULL;
struct tm tm, copy;
time_t x;
usec_t x_usec, plus = 0, minus = 0, ret;
int r, weekday = -1;
int r, weekday = -1, dst = -1;
unsigned i;
/*
......@@ -609,15 +671,55 @@ int parse_timestamp(const char *t, usec_t *usec) {
goto finish;
}
/* See if the timestamp is suffixed with UTC */
utc = endswith_no_case(t, " UTC");
if (utc)
t = strndupa(t, utc - t);
else {
const char *e = NULL;
int j;
tzset();
/* See if the timestamp is suffixed by either the DST or non-DST local timezone. Note that we only
* support the local timezones here, nothing else. Not because we wouldn't want to, but simply because
* there are no nice APIs available to cover this. By accepting the local time zone strings, we make
* sure that all timestamps written by format_timestamp() can be parsed correctly, even though we don't
* support arbitrary timezone specifications. */