You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
279 lines
6.2 KiB
279 lines
6.2 KiB
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ |
|
|
|
/*** |
|
This file is part of systemd. |
|
|
|
Copyright 2010 Lennart Poettering |
|
|
|
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 <string.h> |
|
#include <unistd.h> |
|
#include <errno.h> |
|
#include <fcntl.h> |
|
#include <sys/types.h> |
|
#include <sys/syscall.h> |
|
|
|
#include "util.h" |
|
|
|
static inline pid_t gettid(void) { |
|
return (pid_t) syscall(SYS_gettid); |
|
} |
|
|
|
size_t page_size(void) { |
|
static __thread size_t pgsz = 0; |
|
long r; |
|
|
|
if (_likely_(pgsz > 0)) |
|
return pgsz; |
|
|
|
assert_se((r = sysconf(_SC_PAGESIZE)) > 0); |
|
|
|
pgsz = (size_t) r; |
|
|
|
return pgsz; |
|
} |
|
|
|
bool endswith(const char *s, const char *postfix) { |
|
size_t sl, pl; |
|
|
|
assert(s); |
|
assert(postfix); |
|
|
|
sl = strlen(s); |
|
pl = strlen(postfix); |
|
|
|
if (pl == 0) |
|
return true; |
|
|
|
if (sl < pl) |
|
return false; |
|
|
|
return memcmp(s + sl - pl, postfix, pl) == 0; |
|
} |
|
int close_nointr(int fd) { |
|
assert(fd >= 0); |
|
|
|
for (;;) { |
|
int r; |
|
|
|
r = close(fd); |
|
if (r >= 0) |
|
return r; |
|
|
|
if (errno != EINTR) |
|
return -errno; |
|
} |
|
} |
|
|
|
void close_nointr_nofail(int fd) { |
|
int saved_errno = errno; |
|
|
|
/* like close_nointr() but cannot fail, and guarantees errno |
|
* is unchanged */ |
|
|
|
assert_se(close_nointr(fd) == 0); |
|
|
|
errno = saved_errno; |
|
} |
|
|
|
int open_terminal(const char *name, int mode) { |
|
int fd, r; |
|
unsigned c = 0; |
|
|
|
/* |
|
* If a TTY is in the process of being closed opening it might |
|
* cause EIO. This is horribly awful, but unlikely to be |
|
* changed in the kernel. Hence we work around this problem by |
|
* retrying a couple of times. |
|
* |
|
* https://bugs.launchpad.net/ubuntu/+source/linux/+bug/554172/comments/245 |
|
*/ |
|
|
|
for (;;) { |
|
if ((fd = open(name, mode)) >= 0) |
|
break; |
|
|
|
if (errno != EIO) |
|
return -errno; |
|
|
|
if (c >= 20) |
|
return -errno; |
|
|
|
usleep(50 * USEC_PER_MSEC); |
|
c++; |
|
} |
|
|
|
if (fd < 0) |
|
return -errno; |
|
|
|
if ((r = isatty(fd)) < 0) { |
|
close_nointr_nofail(fd); |
|
return -errno; |
|
} |
|
|
|
if (!r) { |
|
close_nointr_nofail(fd); |
|
return -ENOTTY; |
|
} |
|
|
|
return fd; |
|
} |
|
|
|
bool streq_ptr(const char *a, const char *b) { |
|
|
|
/* Like streq(), but tries to make sense of NULL pointers */ |
|
|
|
if (a && b) |
|
return streq(a, b); |
|
|
|
if (!a && !b) |
|
return true; |
|
|
|
return false; |
|
} |
|
bool is_main_thread(void) { |
|
static __thread int cached = 0; |
|
|
|
if (_unlikely_(cached == 0)) |
|
cached = getpid() == gettid() ? 1 : -1; |
|
|
|
return cached > 0; |
|
} |
|
|
|
int safe_atou(const char *s, unsigned *ret_u) { |
|
char *x = NULL; |
|
unsigned long l; |
|
|
|
assert(s); |
|
assert(ret_u); |
|
|
|
errno = 0; |
|
l = strtoul(s, &x, 0); |
|
|
|
if (!x || *x || errno) |
|
return errno ? -errno : -EINVAL; |
|
|
|
if ((unsigned long) (unsigned) l != l) |
|
return -ERANGE; |
|
|
|
*ret_u = (unsigned) l; |
|
return 0; |
|
} |
|
|
|
static const char *const log_level_table[] = { |
|
[LOG_EMERG] = "emerg", |
|
[LOG_ALERT] = "alert", |
|
[LOG_CRIT] = "crit", |
|
[LOG_ERR] = "err", |
|
[LOG_WARNING] = "warning", |
|
[LOG_NOTICE] = "notice", |
|
[LOG_INFO] = "info", |
|
[LOG_DEBUG] = "debug" |
|
}; |
|
|
|
DEFINE_STRING_TABLE_LOOKUP(log_level, int); |
|
|
|
char *strnappend(const char *s, const char *suffix, size_t b) { |
|
size_t a; |
|
char *r; |
|
|
|
if (!s && !suffix) |
|
return strdup(""); |
|
|
|
if (!s) |
|
return strndup(suffix, b); |
|
|
|
if (!suffix) |
|
return strdup(s); |
|
|
|
assert(s); |
|
assert(suffix); |
|
|
|
a = strlen(s); |
|
if (b > ((size_t) -1) - a) |
|
return NULL; |
|
|
|
r = new(char, a+b+1); |
|
if (!r) |
|
return NULL; |
|
|
|
memcpy(r, s, a); |
|
memcpy(r+a, suffix, b); |
|
r[a+b] = 0; |
|
|
|
return r; |
|
} |
|
|
|
char *strappend(const char *s, const char *suffix) { |
|
return strnappend(s, suffix, suffix ? strlen(suffix) : 0); |
|
} |
|
|
|
char *strjoin(const char *x, ...) { |
|
va_list ap; |
|
size_t l; |
|
char *r, *p; |
|
|
|
va_start(ap, x); |
|
|
|
if (x) { |
|
l = strlen(x); |
|
|
|
for (;;) { |
|
const char *t; |
|
size_t n; |
|
|
|
t = va_arg(ap, const char *); |
|
if (!t) |
|
break; |
|
|
|
n = strlen(t); |
|
if (n > ((size_t) -1) - l) { |
|
va_end(ap); |
|
return NULL; |
|
} |
|
|
|
l += n; |
|
} |
|
} else |
|
l = 0; |
|
|
|
va_end(ap); |
|
|
|
r = new(char, l+1); |
|
if (!r) |
|
return NULL; |
|
|
|
if (x) { |
|
p = stpcpy(r, x); |
|
|
|
va_start(ap, x); |
|
|
|
for (;;) { |
|
const char *t; |
|
|
|
t = va_arg(ap, const char *); |
|
if (!t) |
|
break; |
|
|
|
p = stpcpy(p, t); |
|
} |
|
|
|
va_end(ap); |
|
} else |
|
r[0] = 0; |
|
|
|
return r; |
|
}
|
|
|