#include <sys/utsname.h>
#include <pwd.h>
#include <netinet/ip.h>
+#include <linux/kd.h>
#include "macro.h"
#include "util.h"
#include "log.h"
#include "strv.h"
+#ifdef HAVE_SELINUX
+#include <selinux/selinux.h>
+#include <selinux/label.h>
+
+static struct selabel_handle *label_hnd = NULL;
+
+static inline bool use_selinux(void) {
+ static int use_selinux_ind = -1;
+
+ if (use_selinux_ind < 0)
+ use_selinux_ind = is_selinux_enabled() > 0;
+
+ return use_selinux_ind;
+}
+
+static int label_get_file_label_from_path(
+ const char *label,
+ const char *path,
+ const char *class,
+ security_context_t *fcon) {
+
+ security_context_t dir_con = NULL;
+ security_class_t sclass;
+ int r = 0;
+
+ r = getfilecon(path, &dir_con);
+ if (r >= 0) {
+ r = -1;
+ errno = EINVAL;
+
+ if ((sclass = string_to_security_class(class)) != 0)
+ r = security_compute_create((security_context_t) label, dir_con, sclass, fcon);
+ }
+ if (r < 0)
+ r = -errno;
+
+ freecon(dir_con);
+ return r;
+}
+
+#endif
+
+int label_init(void) {
+ int r = 0;
+
+#ifdef HAVE_SELINUX
+
+ if (!use_selinux())
+ return 0;
+
+ label_hnd = selabel_open(SELABEL_CTX_FILE, NULL, 0);
+ if (!label_hnd) {
+ log_full(security_getenforce() == 1 ? LOG_ERR : LOG_DEBUG,
+ "Failed to initialize SELinux context: %m");
+ r = (security_getenforce() == 1) ? -errno : 0;
+ }
+#endif
+
+ return r;
+}
+
+int label_fix(const char *path) {
+ int r = 0;
+
+#ifdef HAVE_SELINUX
+ struct stat st;
+ security_context_t fcon;
+
+ if (!use_selinux() || !label_hnd)
+ return 0;
+
+ r = lstat(path, &st);
+ if (r == 0) {
+ r = selabel_lookup_raw(label_hnd, &fcon, path, st.st_mode);
+
+ if (r == 0) {
+ r = setfilecon(path, fcon);
+ freecon(fcon);
+ }
+ }
+ if (r < 0) {
+ log_full(security_getenforce() == 1 ? LOG_ERR : LOG_DEBUG,
+ "Unable to fix label of %s: %m", path);
+ r = (security_getenforce() == 1) ? -errno : 0;
+ }
+#endif
+
+ return r;
+}
+
+void label_finish(void) {
+
+#ifdef HAVE_SELINUX
+ if (use_selinux() && label_hnd)
+ selabel_close(label_hnd);
+#endif
+}
+
+int label_get_socket_label_from_exe(const char *exe, char **label) {
+
+ int r = 0;
+
+#ifdef HAVE_SELINUX
+ security_context_t mycon = NULL, fcon = NULL;
+ security_class_t sclass;
+
+ if (!use_selinux()) {
+ *label = NULL;
+ return 0;
+ }
+
+ r = getcon(&mycon);
+ if (r < 0)
+ goto fail;
+
+ r = getfilecon(exe, &fcon);
+ if (r < 0)
+ goto fail;
+
+ sclass = string_to_security_class("process");
+ r = security_compute_create(mycon, fcon, sclass, (security_context_t *) label);
+ if (r == 0)
+ log_debug("SELinux Socket context for %s will be set to %s", exe, *label);
+
+fail:
+ if (r < 0 && security_getenforce() == 1)
+ r = -errno;
+
+ freecon(mycon);
+ freecon(fcon);
+#endif
+
+ return r;
+}
+
+int label_fifofile_set(const char *label, const char *path) {
+ int r = 0;
+
+#ifdef HAVE_SELINUX
+ security_context_t filecon = NULL;
+
+ if (!use_selinux() || !label)
+ return 0;
+
+ if (((r = label_get_file_label_from_path(label, path, "fifo_file", &filecon)) == 0)) {
+ if ((r = setfscreatecon(filecon)) < 0) {
+ log_error("Failed to set SELinux file context (%s) on %s: %m", label, path);
+ r = -errno;
+ }
+
+ freecon(filecon);
+ }
+
+ if (r < 0 && security_getenforce() == 0)
+ r = 0;
+#endif
+
+ return r;
+}
+
+int label_socket_set(const char *label) {
+
+#ifdef HAVE_SELINUX
+ if (!use_selinux())
+ return 0;
+
+ if (setsockcreatecon((security_context_t) label) < 0) {
+ log_full(security_getenforce() == 1 ? LOG_ERR : LOG_DEBUG,
+ "Failed to set SELinux context (%s) on socket: %m", label);
+
+ if (security_getenforce() == 1)
+ return -errno;
+ }
+#endif
+
+ return 0;
+}
+
+void label_file_clear(void) {
+
+#ifdef HAVE_SELINUX
+ if (!use_selinux())
+ return;
+
+ setfscreatecon(NULL);
+#endif
+}
+
+void label_socket_clear(void) {
+
+#ifdef HAVE_SELINUX
+ if (!use_selinux())
+ return;
+
+ setsockcreatecon(NULL);
+#endif
+}
+
+void label_free(const char *label) {
+
+#ifdef HAVE_SELINUX
+ if (!use_selinux())
+ return;
+
+ freecon((security_context_t) label);
+#endif
+}
+
+static int label_mkdir(
+ const char *path,
+ mode_t mode) {
+
+#ifdef HAVE_SELINUX
+ int r;
+ security_context_t fcon = NULL;
+
+ if (use_selinux() && label_hnd) {
+ if (path[0] == '/') {
+ r = selabel_lookup_raw(label_hnd, &fcon, path, mode);
+ }
+ else {
+ char *cwd = NULL;
+ char *newpath = NULL;
+ cwd = getcwd(NULL,0);
+ if ((! cwd) || (asprintf(&newpath, "%s/%s",cwd,path) < 0)) {
+ free(cwd);
+ return -errno;
+ }
+ r = selabel_lookup_raw(label_hnd, &fcon, newpath, mode);
+ free(cwd);
+ free(newpath);
+ }
+
+ if (r == 0)
+ r = setfscreatecon(fcon);
+
+ if ((r < 0) && (errno != ENOENT)) {
+ log_error("Failed to set security context %s for %s", fcon, path);
+
+ if (security_getenforce() == 1)
+ goto finish;
+ }
+ }
+ r = mkdir(path, mode);
+
+finish:
+ if (use_selinux() && label_hnd) {
+ setfscreatecon(NULL);
+ freecon(fcon);
+ }
+
+ return r;
+#else
+ return mkdir(path, mode);
+#endif
+}
+
bool streq_ptr(const char *a, const char *b) {
/* Like streq(), but tries to make sense of NULL pointers */
int safe_mkdir(const char *path, mode_t mode, uid_t uid, gid_t gid) {
struct stat st;
- if (mkdir(path, mode) >= 0)
+ if (label_mkdir(path, mode) >= 0)
if (chmod_and_chown(path, mode, uid, gid) < 0)
return -errno;
if (!(t = strndup(path, e - path)))
return -ENOMEM;
- r = mkdir(t, mode);
+ r = label_mkdir(t, mode);
free(t);
if (r < 0 && errno != EEXIST)
if ((r = mkdir_parents(path, mode)) < 0)
return r;
- if (mkdir(path, mode) < 0 && errno != EEXIST)
+ if (label_mkdir(path, mode) < 0 && errno != EEXIST)
return -errno;
return 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 reset_terminal(int fd) {
struct termios termios;
int r = 0;
+ long arg;
+
+ /* Set terminal to some sane defaults */
assert(fd >= 0);
- /* Set terminal to some sane defaults */
+ /* First, unlock termios */
+ zero(termios);
+ ioctl(fd, TIOCSLCKTRMIOS, &termios);
+
+ /* Disable exclusive mode, just in case */
+ ioctl(fd, TIOCNXCL);
+
+ /* Enable console unicode mode */
+ arg = K_UNICODE;
+ ioctl(fd, KDSKBMODE, &arg);
if (tcgetattr(fd, &termios) < 0) {
r = -errno;
}
if (e.wd != wd || !(e.mask & IN_CLOSE)) {
- r = -errno;
+ r = -EIO;
goto fail;
}
if (!(d = fdopendir(fd))) {
close_nointr_nofail(fd);
- return -errno;
+
+ return errno == ENOENT ? 0 : -errno;
}
for (;;) {
struct stat st;
if (fstatat(fd, de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
- if (ret == 0)
+ if (ret == 0 && errno != ENOENT)
ret = -errno;
continue;
}
int subdir_fd;
if ((subdir_fd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC)) < 0) {
- if (ret == 0)
+ if (ret == 0 && errno != ENOENT)
ret = -errno;
continue;
}
}
if (unlinkat(fd, de->d_name, AT_REMOVEDIR) < 0) {
- if (ret == 0)
+ if (ret == 0 && errno != ENOENT)
ret = -errno;
}
} else if (!only_dirs) {
if (unlinkat(fd, de->d_name, 0) < 0) {
- if (ret == 0)
+ if (ret == 0 && errno != ENOENT)
ret = -errno;
}
}
char *replace_env(const char *format, char **env) {
enum {
WORD,
- DOLLAR,
+ CURLY,
VARIABLE
} state = WORD;
case WORD:
if (*e == '$')
- state = DOLLAR;
+ state = CURLY;
break;
- case DOLLAR:
- if (*e == '(') {
+ case CURLY:
+ if (*e == '{') {
if (!(k = strnappend(r, word, e-word-1)))
goto fail;
break;
case VARIABLE:
- if (*e == ')') {
- char *t;
+ if (*e == '}') {
+ const char *t;
- if ((t = strv_env_get_with_length(env, word+2, e-word-2))) {
- if (!(k = strappend(r, t)))
- goto fail;
+ if (!(t = strv_env_get_with_length(env, word+2, e-word-2)))
+ t = "";
- free(r);
- r = k;
+ if (!(k = strappend(r, t)))
+ goto fail;
- word = e+1;
- }
+ free(r);
+ r = k;
+ word = e+1;
state = WORD;
}
break;
char **replace_env_argv(char **argv, char **env) {
char **r, **i;
- unsigned k = 0;
+ unsigned k = 0, l = 0;
+
+ l = strv_length(argv);
- if (!(r = new(char*, strv_length(argv)+1)))
+ if (!(r = new(char*, l+1)))
return NULL;
STRV_FOREACH(i, argv) {
+
+ /* If $FOO appears as single word, replace it by the split up variable */
+ if ((*i)[0] == '$' && (*i)[1] != '{') {
+ char *e;
+ char **w, **m;
+ unsigned q;
+
+ if ((e = strv_env_get(env, *i+1))) {
+
+ if (!(m = strv_split_quoted(e))) {
+ r[k] = NULL;
+ strv_free(r);
+ return NULL;
+ }
+ } else
+ m = NULL;
+
+ q = strv_length(m);
+ l = l + q - 1;
+
+ if (!(w = realloc(r, sizeof(char*) * (l+1)))) {
+ r[k] = NULL;
+ strv_free(r);
+ strv_free(m);
+ return NULL;
+ }
+
+ r = w;
+ if (m) {
+ memcpy(r + k, m, q * sizeof(char*));
+ free(m);
+ }
+
+ k += q;
+ continue;
+ }
+
+ /* If ${FOO} appears as part of a word, replace it by the variable as-is */
if (!(r[k++] = replace_env(*i, env))) {
strv_free(r);
return NULL;
a.st_ino != b.st_ino;
}
+char *ellipsize(const char *s, unsigned length, unsigned percent) {
+ size_t l, x;
+ char *r;
+
+ assert(s);
+ assert(percent <= 100);
+ assert(length >= 3);
+
+ l = strlen(s);
+
+ if (l <= 3 || l <= length)
+ return strdup(s);
+
+ if (!(r = new0(char, length+1)))
+ return r;
+
+ x = (length * percent) / 100;
+
+ if (x > length - 3)
+ x = length - 3;
+
+ memcpy(r, s, x);
+ r[x] = '.';
+ r[x+1] = '.';
+ r[x+2] = '.';
+ memcpy(r + x + 3,
+ s + l - (length - x - 3),
+ length - x - 3);
+
+ return r;
+}
+
static const char *const ioprio_class_table[] = {
[IOPRIO_CLASS_NONE] = "none",
[IOPRIO_CLASS_RT] = "realtime",