#include "path-util.h"
#include "exit-status.h"
#include "hashmap.h"
+#include "env-util.h"
int saved_argc = 0;
char **saved_argv = NULL;
static volatile unsigned cached_columns = 0;
static volatile unsigned cached_lines = 0;
-bool is_efiboot(void) {
- return access("/sys/firmware/efi", F_OK) >= 0;
-}
-
size_t page_size(void) {
static __thread size_t pgsz = 0;
long r;
return false;
}
-usec_t now(clockid_t clock_id) {
- struct timespec ts;
-
- assert_se(clock_gettime(clock_id, &ts) == 0);
-
- return timespec_load(&ts);
-}
-
-dual_timestamp* dual_timestamp_get(dual_timestamp *ts) {
- assert(ts);
-
- ts->realtime = now(CLOCK_REALTIME);
- ts->monotonic = now(CLOCK_MONOTONIC);
-
- return ts;
-}
-
-dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u) {
- int64_t delta;
- assert(ts);
-
- ts->realtime = u;
-
- if (u == 0)
- ts->monotonic = 0;
- else {
- delta = (int64_t) now(CLOCK_REALTIME) - (int64_t) u;
-
- ts->monotonic = now(CLOCK_MONOTONIC);
-
- if ((int64_t) ts->monotonic > delta)
- ts->monotonic -= delta;
- else
- ts->monotonic = 0;
- }
-
- return ts;
-}
-
-usec_t timespec_load(const struct timespec *ts) {
- assert(ts);
-
- if (ts->tv_sec == (time_t) -1 &&
- ts->tv_nsec == (long) -1)
- return (usec_t) -1;
-
- if ((usec_t) ts->tv_sec > (UINT64_MAX - (ts->tv_nsec / NSEC_PER_USEC)) / USEC_PER_SEC)
- return (usec_t) -1;
-
- return
- (usec_t) ts->tv_sec * USEC_PER_SEC +
- (usec_t) ts->tv_nsec / NSEC_PER_USEC;
-}
-
-struct timespec *timespec_store(struct timespec *ts, usec_t u) {
- assert(ts);
-
- if (u == (usec_t) -1) {
- ts->tv_sec = (time_t) -1;
- ts->tv_nsec = (long) -1;
- return ts;
- }
-
- ts->tv_sec = (time_t) (u / USEC_PER_SEC);
- ts->tv_nsec = (long int) ((u % USEC_PER_SEC) * NSEC_PER_USEC);
-
- return ts;
-}
-
-usec_t timeval_load(const struct timeval *tv) {
- assert(tv);
-
- if (tv->tv_sec == (time_t) -1 &&
- tv->tv_usec == (suseconds_t) -1)
- return (usec_t) -1;
-
- if ((usec_t) tv->tv_sec > (UINT64_MAX - tv->tv_usec) / USEC_PER_SEC)
- return (usec_t) -1;
-
- return
- (usec_t) tv->tv_sec * USEC_PER_SEC +
- (usec_t) tv->tv_usec;
-}
-
-struct timeval *timeval_store(struct timeval *tv, usec_t u) {
- assert(tv);
-
- if (u == (usec_t) -1) {
- tv->tv_sec = (time_t) -1;
- tv->tv_usec = (suseconds_t) -1;
- return tv;
- }
-
- tv->tv_sec = (time_t) (u / USEC_PER_SEC);
- tv->tv_usec = (suseconds_t) (u % USEC_PER_SEC);
-
- return tv;
-}
-
char* endswith(const char *s, const char *postfix) {
size_t sl, pl;
}
int close_nointr(int fd) {
- assert(fd >= 0);
-
- for (;;) {
- int r;
+ int r;
- r = close(fd);
- if (r >= 0)
- return r;
+ assert(fd >= 0);
+ r = close(fd);
- if (errno != EINTR)
- return -errno;
- }
+ /* Just ignore EINTR; a retry loop is the wrong
+ * thing to do on Linux.
+ *
+ * http://lkml.indiana.edu/hypermail/linux/kernel/0509.1/0877.html
+ * https://bugzilla.gnome.org/show_bug.cgi?id=682819
+ * http://utcc.utoronto.ca/~cks/space/blog/unix/CloseEINTR
+ * https://sites.google.com/site/michaelsafyan/software-engineering/checkforeintrwheninvokingclosethinkagain
+ */
+ if (_unlikely_(r < 0 && errno == EINTR))
+ return 0;
+ else if (r >= 0)
+ return r;
+ else
+ return -errno;
}
void close_nointr_nofail(int fd) {
int parse_boolean(const char *v) {
assert(v);
- if (streq(v, "1") || v[0] == 'y' || v[0] == 'Y' || v[0] == 't' || v[0] == 'T' || !strcasecmp(v, "on"))
+ if (streq(v, "1") || v[0] == 'y' || v[0] == 'Y' || v[0] == 't' || v[0] == 'T' || strcaseeq(v, "on"))
return 1;
- else if (streq(v, "0") || v[0] == 'n' || v[0] == 'N' || v[0] == 'f' || v[0] == 'F' || !strcasecmp(v, "off"))
+ else if (streq(v, "0") || v[0] == 'n' || v[0] == 'N' || v[0] == 'f' || v[0] == 'F' || strcaseeq(v, "off"))
return 0;
return -EINVAL;
if (!fgets(line, sizeof(line), f)) {
r = feof(f) ? -EIO : -errno;
- fclose(f);
return r;
}
}
int write_one_line_file_atomic(const char *fn, const char *line) {
- FILE *f;
+ _cleanup_fclose_ FILE *f = NULL;
+ _cleanup_free_ char *p = NULL;
int r;
- char *p;
assert(fn);
assert(line);
fflush(f);
- if (ferror(f)) {
- if (errno != 0)
- r = -errno;
- else
- r = -EIO;
- } else {
+ if (ferror(f))
+ r = errno ? -errno : -EIO;
+ else {
if (rename(p, fn) < 0)
r = -errno;
else
if (r < 0)
unlink(p);
- fclose(f);
- free(p);
-
return r;
}
_cleanup_free_ char *buf = NULL;
struct stat st;
+ assert(fn);
+ assert(contents);
+
f = fopen(fn, "re");
if (!f)
return -errno;
value = va_arg(ap, char **);
n = strlen(key);
- if (strncmp(p, key, n) != 0 ||
+ if (!strneq(p, key, n) ||
p[n] != '=')
continue;
return r;
}
-int load_env_file(
- const char *fname,
- char ***rl) {
+int load_env_file(const char *fname, char ***rl) {
- FILE *f;
- char **m = NULL;
- int r;
+ _cleanup_fclose_ FILE *f;
+ _cleanup_strv_free_ char **m = NULL;
+ _cleanup_free_ char *c = NULL;
assert(fname);
assert(rl);
- if (!(f = fopen(fname, "re")))
+ /* This reads an environment file, but will not complain about
+ * any invalid assignments, that needs to be done by the
+ * caller */
+
+ f = fopen(fname, "re");
+ if (!f)
return -errno;
while (!feof(f)) {
- char l[LINE_MAX], *p, *u;
- char **t;
+ char l[LINE_MAX], *p, *cs, *b;
if (!fgets(l, sizeof(l), f)) {
- if (feof(f))
- break;
+ if (ferror(f))
+ return -errno;
- r = -errno;
- goto finish;
+ /* The previous line was a continuation line?
+ * Let's process it now, before we leave the
+ * loop */
+ if (c)
+ goto process;
+
+ break;
}
- p = strstrip(l);
+ /* Is this a continuation line? If so, just append
+ * this to c, and go to next line right-away */
+ cs = endswith(l, "\\\n");
+ if (cs) {
+ *cs = '\0';
+ b = strappend(c, l);
+ if (!b)
+ return -ENOMEM;
- if (!*p)
+ free(c);
+ c = b;
continue;
+ }
- if (strchr(COMMENTS, *p))
- continue;
+ /* If the previous line was a continuation line,
+ * append the current line to it */
+ if (c) {
+ b = strappend(c, l);
+ if (!b)
+ return -ENOMEM;
- if (!(u = normalize_env_assignment(p))) {
- r = log_oom();
- goto finish;
+ free(c);
+ c = b;
}
- t = strv_append(m, u);
- free(u);
+ process:
+ p = strstrip(c ? c : l);
- if (!t) {
- r = log_oom();
- goto finish;
+ if (*p && !strchr(COMMENTS, *p)) {
+ _cleanup_free_ char *u;
+ int k;
+
+ u = normalize_env_assignment(p);
+ if (!u)
+ return -ENOMEM;
+
+ k = strv_extend(&m, u);
+ if (k < 0)
+ return -ENOMEM;
}
- strv_free(m);
- m = t;
+ free(c);
+ c = NULL;
}
- r = 0;
-
*rl = m;
m = NULL;
-finish:
- if (f)
- fclose(f);
-
- strv_free(m);
-
- return r;
+ return 0;
}
int write_env_file(const char *fname, char **l) {
}
int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char **line) {
- char *r, *k;
+ char *r = NULL, *k;
int c;
- bool space = false;
- size_t left;
FILE *f;
- assert(max_length > 0);
assert(line);
if (pid == 0)
if (!f)
return -errno;
+ if (max_length == 0) {
+ size_t len = 1;
+ while ((c = getc(f)) != EOF) {
+ k = realloc(r, len+1);
+ if (k == NULL) {
+ free(r);
+ fclose(f);
+ return -ENOMEM;
+ }
+ r = k;
+ r[len-1] = isprint(c) ? c : ' ';
+ r[len] = 0;
+ len++;
+ }
+ } else {
+ bool space = false;
+ size_t left;
+ r = new(char, max_length);
+ if (!r) {
+ fclose(f);
+ return -ENOMEM;
+ }
- r = new(char, max_length);
- if (!r) {
- fclose(f);
- return -ENOMEM;
- }
+ k = r;
+ left = max_length;
+ while ((c = getc(f)) != EOF) {
- k = r;
- left = max_length;
- while ((c = getc(f)) != EOF) {
+ if (isprint(c)) {
+ if (space) {
+ if (left <= 4)
+ break;
+
+ *(k++) = ' ';
+ left--;
+ space = false;
+ }
- if (isprint(c)) {
- if (space) {
if (left <= 4)
break;
- *(k++) = ' ';
+ *(k++) = (char) c;
left--;
- space = false;
- }
-
- if (left <= 4)
- break;
+ } else
+ space = true;
+ }
- *(k++) = (char) c;
- left--;
- } else
- space = true;
+ if (left <= 4) {
+ size_t n = MIN(left-1, 3U);
+ memcpy(k, "...", n);
+ k[n] = 0;
+ } else
+ *k = 0;
}
- if (left <= 4) {
- size_t n = MIN(left-1, 3U);
- memcpy(k, "...", n);
- k[n] = 0;
- } else
- *k = 0;
-
fclose(f);
/* Kernel threads have no argv[] */
- if (r[0] == 0) {
+ if (r == NULL || r[0] == 0) {
char *t;
int h;
}
static int get_process_id(pid_t pid, const char *field, uid_t *uid) {
- char *p;
- FILE *f;
- int r;
+ _cleanup_fclose_ FILE *f = NULL;
+ _cleanup_free_ char *p = NULL;
+ assert(field);
assert(uid);
if (pid == 0)
return -ENOMEM;
f = fopen(p, "re");
- free(p);
-
if (!f)
return -errno;
- while (!feof(f)) {
- char line[LINE_MAX], *l;
-
- if (!fgets(line, sizeof(line), f)) {
- if (feof(f))
- break;
-
- r = -errno;
- goto finish;
- }
+ FOREACH_LINE(f, line, return -errno) {
+ char *l;
l = strstrip(line);
l[strcspn(l, WHITESPACE)] = 0;
- r = parse_uid(l, uid);
- goto finish;
+ return parse_uid(l, uid);
}
}
- r = -EIO;
-
-finish:
- fclose(f);
-
- return r;
+ return -EIO;
}
int get_process_uid(pid_t pid, uid_t *uid) {
return false;
}
-char *format_timestamp(char *buf, size_t l, usec_t t) {
- struct tm tm;
- time_t sec;
-
- assert(buf);
- assert(l > 0);
-
- if (t <= 0)
- return NULL;
-
- sec = (time_t) (t / USEC_PER_SEC);
-
- if (strftime(buf, l, "%a, %Y-%m-%d %H:%M:%S %Z", localtime_r(&sec, &tm)) <= 0)
- return NULL;
-
- return buf;
-}
-
-char *format_timestamp_pretty(char *buf, size_t l, usec_t t) {
- usec_t n, d;
-
- n = now(CLOCK_REALTIME);
-
- if (t <= 0 || t > n || t + USEC_PER_DAY*7 <= t)
- return NULL;
-
- d = n - t;
-
- if (d >= USEC_PER_YEAR)
- snprintf(buf, l, "%llu years and %llu months ago",
- (unsigned long long) (d / USEC_PER_YEAR),
- (unsigned long long) ((d % USEC_PER_YEAR) / USEC_PER_MONTH));
- else if (d >= USEC_PER_MONTH)
- snprintf(buf, l, "%llu months and %llu days ago",
- (unsigned long long) (d / USEC_PER_MONTH),
- (unsigned long long) ((d % USEC_PER_MONTH) / USEC_PER_DAY));
- else if (d >= USEC_PER_WEEK)
- snprintf(buf, l, "%llu weeks and %llu days ago",
- (unsigned long long) (d / USEC_PER_WEEK),
- (unsigned long long) ((d % USEC_PER_WEEK) / USEC_PER_DAY));
- else if (d >= 2*USEC_PER_DAY)
- snprintf(buf, l, "%llu days ago", (unsigned long long) (d / USEC_PER_DAY));
- else if (d >= 25*USEC_PER_HOUR)
- snprintf(buf, l, "1 day and %lluh ago",
- (unsigned long long) ((d - USEC_PER_DAY) / USEC_PER_HOUR));
- else if (d >= 6*USEC_PER_HOUR)
- snprintf(buf, l, "%lluh ago",
- (unsigned long long) (d / USEC_PER_HOUR));
- else if (d >= USEC_PER_HOUR)
- snprintf(buf, l, "%lluh %llumin ago",
- (unsigned long long) (d / USEC_PER_HOUR),
- (unsigned long long) ((d % USEC_PER_HOUR) / USEC_PER_MINUTE));
- else if (d >= 5*USEC_PER_MINUTE)
- snprintf(buf, l, "%llumin ago",
- (unsigned long long) (d / USEC_PER_MINUTE));
- else if (d >= USEC_PER_MINUTE)
- snprintf(buf, l, "%llumin %llus ago",
- (unsigned long long) (d / USEC_PER_MINUTE),
- (unsigned long long) ((d % USEC_PER_MINUTE) / USEC_PER_SEC));
- else if (d >= USEC_PER_SEC)
- snprintf(buf, l, "%llus ago",
- (unsigned long long) (d / USEC_PER_SEC));
- else if (d >= USEC_PER_MSEC)
- snprintf(buf, l, "%llums ago",
- (unsigned long long) (d / USEC_PER_MSEC));
- else if (d > 0)
- snprintf(buf, l, "%lluus ago",
- (unsigned long long) d);
- else
- snprintf(buf, l, "now");
-
- buf[l-1] = 0;
- return buf;
-}
-
-char *format_timespan(char *buf, size_t l, usec_t t) {
- static const struct {
- const char *suffix;
- usec_t usec;
- } table[] = {
- { "w", USEC_PER_WEEK },
- { "d", USEC_PER_DAY },
- { "h", USEC_PER_HOUR },
- { "min", USEC_PER_MINUTE },
- { "s", USEC_PER_SEC },
- { "ms", USEC_PER_MSEC },
- { "us", 1 },
- };
-
- unsigned i;
- char *p = buf;
-
- assert(buf);
- assert(l > 0);
-
- if (t == (usec_t) -1)
- return NULL;
-
- if (t == 0) {
- snprintf(p, l, "0");
- p[l-1] = 0;
- return p;
- }
-
- /* The result of this function can be parsed with parse_usec */
-
- for (i = 0; i < ELEMENTSOF(table); i++) {
- int k;
- size_t n;
-
- if (t < table[i].usec)
- continue;
-
- if (l <= 1)
- break;
-
- k = snprintf(p, l, "%s%llu%s", p > buf ? " " : "", (unsigned long long) (t / table[i].usec), table[i].suffix);
- n = MIN((size_t) k, l);
-
- l -= n;
- p += n;
-
- t %= table[i].usec;
- }
-
- *p = 0;
-
- return buf;
-}
-
bool fstype_is_network(const char *fstype) {
static const char table[] =
"cifs\0"
wd = inotify_add_watch(notify, name, IN_CLOSE);
if (wd < 0) {
r = -errno;
+ log_error("Failed to add watch on %s: %m", name);
goto fail;
}
}
continue;
}
- return n > 0 ? n : (k < 0 ? -errno : 0);
- }
-
- p += k;
- nbytes -= k;
- n += k;
- }
-
- return n;
-}
-
-int parse_usec(const char *t, usec_t *usec) {
- static const struct {
- const char *suffix;
- usec_t usec;
- } table[] = {
- { "seconds", USEC_PER_SEC },
- { "second", USEC_PER_SEC },
- { "sec", USEC_PER_SEC },
- { "s", USEC_PER_SEC },
- { "minutes", USEC_PER_MINUTE },
- { "minute", USEC_PER_MINUTE },
- { "min", USEC_PER_MINUTE },
- { "months", USEC_PER_MONTH },
- { "month", USEC_PER_MONTH },
- { "msec", USEC_PER_MSEC },
- { "ms", USEC_PER_MSEC },
- { "m", USEC_PER_MINUTE },
- { "hours", USEC_PER_HOUR },
- { "hour", USEC_PER_HOUR },
- { "hr", USEC_PER_HOUR },
- { "h", USEC_PER_HOUR },
- { "days", USEC_PER_DAY },
- { "day", USEC_PER_DAY },
- { "d", USEC_PER_DAY },
- { "weeks", USEC_PER_WEEK },
- { "week", USEC_PER_WEEK },
- { "w", USEC_PER_WEEK },
- { "years", USEC_PER_YEAR },
- { "year", USEC_PER_YEAR },
- { "y", USEC_PER_YEAR },
- { "usec", 1ULL },
- { "us", 1ULL },
- { "", USEC_PER_SEC }, /* default is sec */
- };
-
- const char *p;
- usec_t r = 0;
-
- assert(t);
- assert(usec);
-
- p = t;
- do {
- long long l;
- char *e;
- unsigned i;
-
- errno = 0;
- l = strtoll(p, &e, 10);
-
- if (errno != 0)
- return -errno;
-
- if (l < 0)
- return -ERANGE;
-
- if (e == p)
- return -EINVAL;
-
- e += strspn(e, WHITESPACE);
-
- for (i = 0; i < ELEMENTSOF(table); i++)
- if (startswith(e, table[i].suffix)) {
- r += (usec_t) l * table[i].usec;
- p = e + strlen(table[i].suffix);
- break;
- }
-
- if (i >= ELEMENTSOF(table))
- return -EINVAL;
-
- } while (*p != 0);
-
- *usec = r;
-
- return 0;
-}
-
-int parse_nsec(const char *t, nsec_t *nsec) {
- static const struct {
- const char *suffix;
- nsec_t nsec;
- } table[] = {
- { "seconds", NSEC_PER_SEC },
- { "second", NSEC_PER_SEC },
- { "sec", NSEC_PER_SEC },
- { "s", NSEC_PER_SEC },
- { "minutes", NSEC_PER_MINUTE },
- { "minute", NSEC_PER_MINUTE },
- { "min", NSEC_PER_MINUTE },
- { "months", NSEC_PER_MONTH },
- { "month", NSEC_PER_MONTH },
- { "msec", NSEC_PER_MSEC },
- { "ms", NSEC_PER_MSEC },
- { "m", NSEC_PER_MINUTE },
- { "hours", NSEC_PER_HOUR },
- { "hour", NSEC_PER_HOUR },
- { "hr", NSEC_PER_HOUR },
- { "h", NSEC_PER_HOUR },
- { "days", NSEC_PER_DAY },
- { "day", NSEC_PER_DAY },
- { "d", NSEC_PER_DAY },
- { "weeks", NSEC_PER_WEEK },
- { "week", NSEC_PER_WEEK },
- { "w", NSEC_PER_WEEK },
- { "years", NSEC_PER_YEAR },
- { "year", NSEC_PER_YEAR },
- { "y", NSEC_PER_YEAR },
- { "usec", NSEC_PER_USEC },
- { "us", NSEC_PER_USEC },
- { "nsec", 1ULL },
- { "ns", 1ULL },
- { "", 1ULL }, /* default is nsec */
- };
-
- const char *p;
- nsec_t r = 0;
-
- assert(t);
- assert(nsec);
-
- p = t;
- do {
- long long l;
- char *e;
- unsigned i;
-
- errno = 0;
- l = strtoll(p, &e, 10);
-
- if (errno != 0)
- return -errno;
-
- if (l < 0)
- return -ERANGE;
-
- if (e == p)
- return -EINVAL;
-
- e += strspn(e, WHITESPACE);
-
- for (i = 0; i < ELEMENTSOF(table); i++)
- if (startswith(e, table[i].suffix)) {
- r += (nsec_t) l * table[i].nsec;
- p = e + strlen(table[i].suffix);
- break;
- }
-
- if (i >= ELEMENTSOF(table))
- return -EINVAL;
-
- } while (*p != 0);
+ return n > 0 ? n : (k < 0 ? -errno : 0);
+ }
- *nsec = r;
+ p += k;
+ nbytes -= k;
+ n += k;
+ }
- return 0;
+ return n;
}
int parse_bytes(const char *t, off_t *bytes) {
&ttynr) != 1)
return -EIO;
+ if (major(ttynr) == 0 && minor(ttynr) == 0)
+ return -ENOENT;
+
*d = (dev_t) ttynr;
return 0;
}
snprintf(fn, sizeof(fn), "/dev/char/%u:%u", major(devnr), minor(devnr));
char_array_0(fn);
- if ((k = readlink_malloc(fn, &s)) < 0) {
+ k = readlink_malloc(fn, &s);
+ if (k < 0) {
if (k != -ENOENT)
return k;
* symlink in /dev/char. Let's return something
* vaguely useful. */
- if (!(b = strdup(fn + 5)))
+ b = strdup(fn + 5);
+ if (!b)
return -ENOMEM;
*r = b;
return ret;
}
+static int is_temporary_fs(struct statfs *s) {
+ assert(s);
+ return s->f_type == TMPFS_MAGIC ||
+ (long)s->f_type == (long)RAMFS_MAGIC;
+}
+
int rm_rf_children(int fd, bool only_dirs, bool honour_sticky, struct stat *root_dev) {
struct statfs s;
/* 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 (s.f_type != TMPFS_MAGIC &&
- s.f_type != RAMFS_MAGIC) {
+ if (!is_temporary_fs(&s)) {
log_error("Attempted to remove disk file system, and we can't allow that.");
close_nointr_nofail(fd);
return -EPERM;
if (statfs(path, &s) < 0)
return -errno;
- if (s.f_type != TMPFS_MAGIC &&
- s.f_type != RAMFS_MAGIC) {
+ if (!is_temporary_fs(&s)) {
log_error("Attempted to remove disk file system, and we can't allow that.");
return -EPERM;
}
return -errno;
}
- if (s.f_type != TMPFS_MAGIC &&
- s.f_type != RAMFS_MAGIC) {
+ if (!is_temporary_fs(&s)) {
log_error("Attempted to remove disk file system, and we can't allow that.");
close_nointr_nofail(fd);
return -EPERM;
if (*e == '}') {
const char *t;
- if (!(t = strv_env_get_with_length(env, word+2, e-word-2)))
- t = "";
+ t = strempty(strv_env_get_n(env, word+2, e-word-2));
- if (!(k = strappend(r, t)))
+ k = strappend(r, t);
+ if (!k)
goto fail;
free(r);
char **w, **m;
unsigned q;
- if ((e = strv_env_get(env, *i+1))) {
+ e = strv_env_get(env, *i+1);
+ if (e) {
if (!(m = strv_split_quoted(e))) {
r[k] = NULL;
unsigned columns(void) {
const char *e;
- unsigned c;
+ int c;
if (_likely_(cached_columns > 0))
return cached_columns;
c = 0;
e = getenv("COLUMNS");
if (e)
- safe_atou(e, &c);
+ safe_atoi(e, &c);
if (c <= 0)
c = fd_columns(STDOUT_FILENO);
return signo;
}
-void dual_timestamp_serialize(FILE *f, const char *name, dual_timestamp *t) {
-
- assert(f);
- assert(name);
- assert(t);
-
- if (!dual_timestamp_is_set(t))
- return;
-
- fprintf(f, "%s=%llu %llu\n",
- name,
- (unsigned long long) t->realtime,
- (unsigned long long) t->monotonic);
-}
-
-void dual_timestamp_deserialize(const char *value, dual_timestamp *t) {
- unsigned long long a, b;
-
- assert(value);
- assert(t);
-
- if (sscanf(value, "%lli %llu", &a, &b) != 2)
- log_debug("Failed to parse finish timestamp value %s", value);
- else {
- t->realtime = a;
- t->monotonic = b;
- }
-}
-
static char *tag_to_udev_node(const char *tagvalue, const char *by) {
char *dn, *t, *u;
int r;
return 0;
}
+char* uid_to_name(uid_t uid) {
+ struct passwd *p;
+ char *r;
+
+ if (uid == 0)
+ return strdup("root");
+
+ p = getpwuid(uid);
+ if (p)
+ return strdup(p->pw_name);
+
+ if (asprintf(&r, "%lu", (unsigned long) uid) < 0)
+ return NULL;
+
+ return r;
+}
+
int get_group_creds(const char **groupname, gid_t *gid) {
struct group *g;
gid_t id;
assert(type);
+ /* If /sys is read-only we cannot sleep */
+ if (access("/sys/power/state", W_OK) < 0)
+ return false;
+
r = read_one_line_file("/sys/power/state", &p);
if (r < 0)
- return r == -ENOENT ? 0 : r;
+ return false;
k = strlen(type);
FOREACH_WORD_SEPARATOR(w, l, p, WHITESPACE, state)
assert(type);
+ /* If /sys is read-only we cannot sleep */
+ if (access("/sys/power/state", W_OK) < 0 ||
+ access("/sys/power/disk", W_OK) < 0)
+ return false;
+
r = read_one_line_file("/sys/power/disk", &p);
if (r < 0)
- return r == -ENOENT ? 0 : r;
+ return false;
k = strlen(type);
FOREACH_WORD_SEPARATOR(w, l, p, WHITESPACE, state) {
saved = access("/etc/initrd-release", F_OK) >= 0 &&
statfs("/", &s) >= 0 &&
- (s.f_type == TMPFS_MAGIC || s.f_type == RAMFS_MAGIC);
+ is_temporary_fs(&s);
return saved;
}
fclose(*f);
}
+void pclosep(FILE **f) {
+ if (*f)
+ pclose(*f);
+}
+
void closep(int *fd) {
if (*fd >= 0)
close_nointr_nofail(*fd);
return true;
}
-int parse_timestamp(const char *t, usec_t *usec) {
- const char *k;
- struct tm tm, copy;
- time_t x;
- usec_t plus = 0, minus = 0, ret;
- int r;
-
- /*
- * Allowed syntaxes:
- *
- * 2012-09-22 16:34:22
- * 2012-09-22 16:34 (seconds will be set to 0)
- * 2012-09-22 (time will be set to 00:00:00)
- * 16:34:22 (date will be set to today)
- * 16:34 (date will be set to today, seconds to 0)
- * now
- * yesterday (time is set to 00:00:00)
- * today (time is set to 00:00:00)
- * tomorrow (time is set to 00:00:00)
- * +5min
- * -5days
- *
- */
-
- assert(t);
- assert(usec);
-
- x = time(NULL);
- assert_se(localtime_r(&x, &tm));
-
- if (streq(t, "now"))
- goto finish;
-
- else if (streq(t, "today")) {
- tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
- goto finish;
-
- } else if (streq(t, "yesterday")) {
- tm.tm_mday --;
- tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
- goto finish;
-
- } else if (streq(t, "tomorrow")) {
- tm.tm_mday ++;
- tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
- goto finish;
-
- } else if (t[0] == '+') {
-
- r = parse_usec(t+1, &plus);
- if (r < 0)
- return r;
-
- goto finish;
- } else if (t[0] == '-') {
-
- r = parse_usec(t+1, &minus);
- if (r < 0)
- return r;
-
- goto finish;
- }
-
- copy = tm;
- k = strptime(t, "%y-%m-%d %H:%M:%S", &tm);
- if (k && *k == 0)
- goto finish;
-
- tm = copy;
- k = strptime(t, "%Y-%m-%d %H:%M:%S", &tm);
- if (k && *k == 0)
- goto finish;
-
- tm = copy;
- k = strptime(t, "%y-%m-%d %H:%M", &tm);
- if (k && *k == 0) {
- tm.tm_sec = 0;
- goto finish;
- }
-
- tm = copy;
- k = strptime(t, "%Y-%m-%d %H:%M", &tm);
- if (k && *k == 0) {
- tm.tm_sec = 0;
- goto finish;
- }
+bool string_has_cc(const char *p) {
+ const char *t;
- tm = copy;
- k = strptime(t, "%y-%m-%d", &tm);
- if (k && *k == 0) {
- tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
- goto finish;
- }
+ assert(p);
- tm = copy;
- k = strptime(t, "%Y-%m-%d", &tm);
- if (k && *k == 0) {
- tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
- goto finish;
- }
+ for (t = p; *t; t++)
+ if (*t > 0 && *t < ' ')
+ return true;
- tm = copy;
- k = strptime(t, "%H:%M:%S", &tm);
- if (k && *k == 0)
- goto finish;
+ return false;
+}
- tm = copy;
- k = strptime(t, "%H:%M", &tm);
- if (k && *k == 0) {
- tm.tm_sec = 0;
- goto finish;
- }
+bool path_is_safe(const char *p) {
- return -EINVAL;
+ if (isempty(p))
+ return false;
-finish:
- x = mktime(&tm);
- if (x == (time_t) -1)
- return -EINVAL;
+ if (streq(p, "..") || startswith(p, "../") || endswith(p, "/..") || strstr(p, "/../"))
+ return false;
- ret = (usec_t) x * USEC_PER_SEC;
+ if (strlen(p) > PATH_MAX)
+ return false;
- ret += plus;
- if (ret > minus)
- ret -= minus;
- else
- ret = 0;
+ /* The following two checks are not really dangerous, but hey, they still are confusing */
+ if (streq(p, ".") || startswith(p, "./") || endswith(p, "/.") || strstr(p, "/./"))
+ return false;
- *usec = ret;
+ if (strstr(p, "//"))
+ return false;
- return 0;
+ return true;
}
/* hey glibc, APIs with callbacks without a user pointer are so useless */
[DRAW_TREE_VERT] = "\342\224\202 ", /* │ */
[DRAW_TREE_BRANCH] = "\342\224\234\342\224\200", /* ├─ */
[DRAW_TREE_RIGHT] = "\342\224\224\342\224\200", /* └─ */
+ [DRAW_TREE_SPACE] = " ", /* */
[DRAW_TRIANGULAR_BULLET] = "\342\200\243 ", /* ‣ */
},
/* ASCII fallback */ {
[DRAW_TREE_VERT] = "| ",
[DRAW_TREE_BRANCH] = "|-",
[DRAW_TREE_RIGHT] = "`-",
+ [DRAW_TREE_SPACE] = " ",
[DRAW_TRIANGULAR_BULLET] = "> ",
}
};
free(r);
return NULL;
}
+
+char *strip_tab_ansi(char **ibuf, size_t *_isz) {
+ const char *i, *begin = NULL;
+ enum {
+ STATE_OTHER,
+ STATE_ESCAPE,
+ STATE_BRACKET
+ } state = STATE_OTHER;
+ char *obuf = NULL;
+ size_t osz = 0, isz;
+ FILE *f;
+
+ assert(ibuf);
+ assert(*ibuf);
+
+ /* Strips ANSI color and replaces TABs by 8 spaces */
+
+ isz = _isz ? *_isz : strlen(*ibuf);
+
+ f = open_memstream(&obuf, &osz);
+ if (!f)
+ return NULL;
+
+ for (i = *ibuf; i < *ibuf + isz + 1; i++) {
+
+ switch (state) {
+
+ case STATE_OTHER:
+ if (i >= *ibuf + isz) /* EOT */
+ break;
+ else if (*i == '\x1B')
+ state = STATE_ESCAPE;
+ else if (*i == '\t')
+ fputs(" ", f);
+ else
+ fputc(*i, f);
+ break;
+
+ case STATE_ESCAPE:
+ if (i >= *ibuf + isz) { /* EOT */
+ fputc('\x1B', f);
+ break;
+ } else if (*i == '[') {
+ state = STATE_BRACKET;
+ begin = i + 1;
+ } else {
+ fputc('\x1B', f);
+ fputc(*i, f);
+ state = STATE_OTHER;
+ }
+
+ break;
+
+ case STATE_BRACKET:
+
+ if (i >= *ibuf + isz || /* EOT */
+ (!(*i >= '0' && *i <= '9') && *i != ';' && *i != 'm')) {
+ fputc('\x1B', f);
+ fputc('[', f);
+ state = STATE_OTHER;
+ i = begin-1;
+ } else if (*i == 'm')
+ state = STATE_OTHER;
+ break;
+ }
+ }
+
+ if (ferror(f)) {
+ fclose(f);
+ free(obuf);
+ return NULL;
+ }
+
+ fclose(f);
+
+ free(*ibuf);
+ *ibuf = obuf;
+
+ if (_isz)
+ *_isz = osz;
+
+ return obuf;
+}
+
+int on_ac_power(void) {
+ bool found_offline = false, found_online = false;
+ _cleanup_closedir_ DIR *d = NULL;
+
+ d = opendir("/sys/class/power_supply");
+ if (!d)
+ return -errno;
+
+ for (;;) {
+ struct dirent *de;
+ union dirent_storage buf;
+ _cleanup_free_ char *p = NULL;
+ _cleanup_close_ int fd = -1, device = -1;
+ char contents[6];
+ ssize_t n;
+ int k;
+
+ k = readdir_r(d, &buf.de, &de);
+ if (k != 0)
+ return -k;
+
+ if (!de)
+ break;
+
+ if (ignore_file(de->d_name))
+ continue;
+
+ device = openat(dirfd(d), de->d_name, O_DIRECTORY|O_RDONLY|O_CLOEXEC|O_NOCTTY);
+ if (device < 0) {
+ if (errno == ENOENT || errno == ENOTDIR)
+ continue;
+
+ return -errno;
+ }
+
+ fd = openat(device, "type", O_RDONLY|O_CLOEXEC|O_NOCTTY);
+ if (fd < 0) {
+ if (errno == ENOENT)
+ continue;
+
+ return -errno;
+ }
+
+ n = read(fd, contents, sizeof(contents));
+ if (n < 0)
+ return -errno;
+
+ if (n != 6 || memcmp(contents, "Mains\n", 6))
+ continue;
+
+ close_nointr_nofail(fd);
+ fd = openat(device, "online", O_RDONLY|O_CLOEXEC|O_NOCTTY);
+ if (fd < 0) {
+ if (errno == ENOENT)
+ continue;
+
+ return -errno;
+ }
+
+ n = read(fd, contents, sizeof(contents));
+ if (n < 0)
+ return -errno;
+
+ if (n != 2 || contents[1] != '\n')
+ return -EIO;
+
+ if (contents[0] == '1') {
+ found_online = true;
+ break;
+ } else if (contents[0] == '0')
+ found_offline = true;
+ else
+ return -EIO;
+ }
+
+ return found_online || !found_offline;
+}
+
+static int search_and_fopen_internal(const char *path, const char *mode, char **search, FILE **_f) {
+ char **i;
+
+ assert(path);
+ assert(mode);
+ assert(_f);
+
+ if (!path_strv_canonicalize_uniq(search))
+ return -ENOMEM;
+
+ STRV_FOREACH(i, search) {
+ _cleanup_free_ char *p = NULL;
+ FILE *f;
+
+ p = strjoin(*i, "/", path, NULL);
+ if (!p)
+ return -ENOMEM;
+
+ f = fopen(p, mode);
+ if (f) {
+ *_f = f;
+ return 0;
+ }
+
+ if (errno != ENOENT)
+ return -errno;
+ }
+
+ return -ENOENT;
+}
+
+int search_and_fopen(const char *path, const char *mode, const char **search, FILE **_f) {
+ _cleanup_strv_free_ char **copy = NULL;
+
+ assert(path);
+ assert(mode);
+ assert(_f);
+
+ if (path_is_absolute(path)) {
+ FILE *f;
+
+ f = fopen(path, mode);
+ if (f) {
+ *_f = f;
+ return 0;
+ }
+
+ return -errno;
+ }
+
+ copy = strv_copy((char**) search);
+ if (!copy)
+ return -ENOMEM;
+
+ return search_and_fopen_internal(path, mode, copy, _f);
+}
+
+int search_and_fopen_nulstr(const char *path, const char *mode, const char *search, FILE **_f) {
+ _cleanup_strv_free_ char **s = NULL;
+
+ if (path_is_absolute(path)) {
+ FILE *f;
+
+ f = fopen(path, mode);
+ if (f) {
+ *_f = f;
+ return 0;
+ }
+
+ return -errno;
+ }
+
+ s = strv_split_nulstr(search);
+ if (!s)
+ return -ENOMEM;
+
+ return search_and_fopen_internal(path, mode, s, _f);
+}