summaryrefslogtreecommitdiff
path: root/src/shared
diff options
context:
space:
mode:
Diffstat (limited to 'src/shared')
-rw-r--r--src/shared/bus-util.c71
-rw-r--r--src/shared/conf-parser.c28
-rw-r--r--src/shared/conf-parser.h2
-rw-r--r--src/shared/machine-image.c119
-rw-r--r--src/shared/pager.c2
-rw-r--r--src/shared/path-lookup.c12
-rw-r--r--src/shared/pty.c633
-rw-r--r--src/shared/pty.h72
8 files changed, 182 insertions, 757 deletions
diff --git a/src/shared/bus-util.c b/src/shared/bus-util.c
index 36c44227c5..fdf41cec19 100644
--- a/src/shared/bus-util.c
+++ b/src/shared/bus-util.c
@@ -23,22 +23,24 @@
#include "sd-daemon.h"
#include "sd-event.h"
-#include "util.h"
-#include "strv.h"
-#include "macro.h"
+#include "sd-bus.h"
+
+#include "bus-error.h"
+#include "bus-internal.h"
+#include "bus-label.h"
+#include "bus-message.h"
+#include "cgroup-util.h"
#include "def.h"
-#include "path-util.h"
+#include "macro.h"
#include "missing.h"
+#include "path-util.h"
#include "set.h"
#include "signal-util.h"
+#include "strv.h"
#include "unit-name.h"
+#include "util.h"
-#include "sd-bus.h"
-#include "bus-error.h"
-#include "bus-label.h"
-#include "bus-message.h"
#include "bus-util.h"
-#include "bus-internal.h"
static int name_owner_change_callback(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
sd_event *e = userdata;
@@ -1421,9 +1423,10 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
return bus_log_create_error(r);
if (STR_IN_SET(field,
- "CPUAccounting", "MemoryAccounting", "BlockIOAccounting",
+ "CPUAccounting", "MemoryAccounting", "BlockIOAccounting", "TasksAccounting",
"SendSIGHUP", "SendSIGKILL", "WakeSystem", "DefaultDependencies",
- "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "RemainAfterExit")) {
+ "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "RemainAfterExit",
+ "PrivateTmp", "PrivateDevices", "PrivateNetwork", "NoNewPrivileges")) {
r = parse_boolean(eq);
if (r < 0) {
@@ -1434,20 +1437,50 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
r = sd_bus_message_append(m, "v", "b", r);
} else if (streq(field, "MemoryLimit")) {
- off_t bytes;
+ uint64_t bytes;
- r = parse_size(eq, 1024, &bytes);
+ if (isempty(eq) || streq(eq, "infinity"))
+ bytes = (uint64_t) -1;
+ else {
+ r = parse_size(eq, 1024, &bytes);
+ if (r < 0) {
+ log_error("Failed to parse bytes specification %s", assignment);
+ return -EINVAL;
+ }
+ }
+
+ r = sd_bus_message_append(m, "v", "t", bytes);
+
+ } else if (streq(field, "TasksMax")) {
+ uint64_t n;
+
+ if (isempty(eq) || streq(eq, "infinity"))
+ n = (uint64_t) -1;
+ else {
+ r = safe_atou64(eq, &n);
+ if (r < 0) {
+ log_error("Failed to parse maximum tasks specification %s", assignment);
+ return -EINVAL;
+ }
+ }
+
+ r = sd_bus_message_append(m, "v", "t", n);
+
+ } else if (STR_IN_SET(field, "CPUShares", "StartupCPUShares")) {
+ uint64_t u;
+
+ r = cg_cpu_shares_parse(eq, &u);
if (r < 0) {
- log_error("Failed to parse bytes specification %s", assignment);
+ log_error("Failed to parse %s value %s.", field, eq);
return -EINVAL;
}
- r = sd_bus_message_append(m, "v", "t", (uint64_t) bytes);
+ r = sd_bus_message_append(m, "v", "t", u);
- } else if (STR_IN_SET(field, "CPUShares", "BlockIOWeight")) {
+ } else if (STR_IN_SET(field, "BlockIOWeight", "StartupBlockIOWeight")) {
uint64_t u;
- r = safe_atou64(eq, &u);
+ r = cg_cpu_shares_parse(eq, &u);
if (r < 0) {
log_error("Failed to parse %s value %s.", field, eq);
return -EINVAL;
@@ -1492,7 +1525,7 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
r = sd_bus_message_append(m, "v", "a(st)", 0);
else {
const char *path, *bandwidth, *e;
- off_t bytes;
+ uint64_t bytes;
e = strchr(eq, ' ');
if (e) {
@@ -1514,7 +1547,7 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
return -EINVAL;
}
- r = sd_bus_message_append(m, "v", "a(st)", 1, path, (uint64_t) bytes);
+ r = sd_bus_message_append(m, "v", "a(st)", 1, path, bytes);
}
} else if (streq(field, "BlockIODeviceWeight")) {
diff --git a/src/shared/conf-parser.c b/src/shared/conf-parser.c
index 23512f0d35..946eac6823 100644
--- a/src/shared/conf-parser.c
+++ b/src/shared/conf-parser.c
@@ -469,7 +469,7 @@ int config_parse_iec_size(const char* unit,
void *userdata) {
size_t *sz = data;
- off_t o;
+ uint64_t v;
int r;
assert(filename);
@@ -477,13 +477,13 @@ int config_parse_iec_size(const char* unit,
assert(rvalue);
assert(data);
- r = parse_size(rvalue, 1024, &o);
- if (r < 0 || (off_t) (size_t) o != o) {
- log_syntax(unit, LOG_ERR, filename, line, r < 0 ? -r : ERANGE, "Failed to parse size value, ignoring: %s", rvalue);
+ r = parse_size(rvalue, 1024, &v);
+ if (r < 0 || (uint64_t) (size_t) v != v) {
+ log_syntax(unit, LOG_ERR, filename, line, r < 0 ? r : ERANGE, "Failed to parse size value, ignoring: %s", rvalue);
return 0;
}
- *sz = (size_t) o;
+ *sz = (size_t) v;
return 0;
}
@@ -499,7 +499,7 @@ int config_parse_si_size(const char* unit,
void *userdata) {
size_t *sz = data;
- off_t o;
+ uint64_t v;
int r;
assert(filename);
@@ -507,17 +507,17 @@ int config_parse_si_size(const char* unit,
assert(rvalue);
assert(data);
- r = parse_size(rvalue, 1000, &o);
- if (r < 0 || (off_t) (size_t) o != o) {
- log_syntax(unit, LOG_ERR, filename, line, r < 0 ? -r : ERANGE, "Failed to parse size value, ignoring: %s", rvalue);
+ r = parse_size(rvalue, 1000, &v);
+ if (r < 0 || (uint64_t) (size_t) v != v) {
+ log_syntax(unit, LOG_ERR, filename, line, r < 0 ? r : ERANGE, "Failed to parse size value, ignoring: %s", rvalue);
return 0;
}
- *sz = (size_t) o;
+ *sz = (size_t) v;
return 0;
}
-int config_parse_iec_off(const char* unit,
+int config_parse_iec_uint64(const char* unit,
const char *filename,
unsigned line,
const char *section,
@@ -528,7 +528,7 @@ int config_parse_iec_off(const char* unit,
void *data,
void *userdata) {
- off_t *bytes = data;
+ uint64_t *bytes = data;
int r;
assert(filename);
@@ -536,11 +536,9 @@ int config_parse_iec_off(const char* unit,
assert(rvalue);
assert(data);
- assert_cc(sizeof(off_t) == sizeof(uint64_t));
-
r = parse_size(rvalue, 1024, bytes);
if (r < 0)
- log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to parse size value, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue);
return 0;
}
diff --git a/src/shared/conf-parser.h b/src/shared/conf-parser.h
index 34e3815782..4efed138c9 100644
--- a/src/shared/conf-parser.h
+++ b/src/shared/conf-parser.h
@@ -109,7 +109,7 @@ int config_parse_uint64(const char *unit, const char *filename, unsigned line, c
int config_parse_double(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_iec_size(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_si_size(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_iec_off(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_iec_uint64(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_bool(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_tristate(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_string(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
diff --git a/src/shared/machine-image.c b/src/shared/machine-image.c
index 70220bdd14..9c1e4d5e13 100644
--- a/src/shared/machine-image.c
+++ b/src/shared/machine-image.c
@@ -19,16 +19,18 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <sys/statfs.h>
-#include <linux/fs.h>
#include <fcntl.h>
+#include <linux/fs.h>
+#include <sys/statfs.h>
-#include "utf8.h"
#include "btrfs-util.h"
-#include "path-util.h"
#include "copy.h"
#include "mkdir.h"
+#include "path-util.h"
#include "rm-rf.h"
+#include "strv.h"
+#include "utf8.h"
+
#include "machine-image.h"
static const char image_search_path[] =
@@ -47,6 +49,38 @@ Image *image_unref(Image *i) {
return NULL;
}
+static char **image_settings_path(Image *image) {
+ _cleanup_strv_free_ char **l = NULL;
+ char **ret;
+ const char *fn, *s;
+ unsigned i = 0;
+
+ assert(image);
+
+ l = new0(char*, 4);
+ if (!l)
+ return NULL;
+
+ fn = strjoina(image->name, ".nspawn");
+
+ FOREACH_STRING(s, "/etc/systemd/nspawn/", "/run/systemd/nspawn/") {
+ l[i] = strappend(s, fn);
+ if (!l[i])
+ return NULL;
+
+ i++;
+ }
+
+ l[i] = file_in_same_dir(image->path, fn);
+ if (!l[i])
+ return NULL;
+
+ ret = l;
+ l = NULL;
+
+ return ret;
+}
+
static int image_new(
ImageType t,
const char *pretty,
@@ -341,6 +375,8 @@ void image_hashmap_free(Hashmap *map) {
int image_remove(Image *i) {
_cleanup_release_lock_file_ LockFile global_lock = LOCK_FILE_INIT, local_lock = LOCK_FILE_INIT;
+ _cleanup_strv_free_ char **settings = NULL;
+ char **j;
int r;
assert(i);
@@ -349,6 +385,10 @@ int image_remove(Image *i) {
path_startswith(i->path, "/usr"))
return -EROFS;
+ settings = image_settings_path(i);
+ if (!settings)
+ return -ENOMEM;
+
/* Make sure we don't interfere with a running nspawn */
r = image_path_lock(i->path, LOCK_EX|LOCK_NB, &global_lock, &local_lock);
if (r < 0)
@@ -357,28 +397,56 @@ int image_remove(Image *i) {
switch (i->type) {
case IMAGE_SUBVOLUME:
- return btrfs_subvol_remove(i->path, true);
+ r = btrfs_subvol_remove(i->path, true);
+ if (r < 0)
+ return r;
+ break;
case IMAGE_DIRECTORY:
/* Allow deletion of read-only directories */
(void) chattr_path(i->path, false, FS_IMMUTABLE_FL);
- return rm_rf(i->path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
+ r = rm_rf(i->path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
+ if (r < 0)
+ return r;
+
+ break;
case IMAGE_RAW:
if (unlink(i->path) < 0)
return -errno;
-
- return 0;
+ break;
default:
return -EOPNOTSUPP;
}
+
+ STRV_FOREACH(j, settings) {
+ if (unlink(*j) < 0 && errno != ENOENT)
+ log_debug_errno(errno, "Failed to unlink %s, ignoring: %m", *j);
+ }
+
+ return 0;
+}
+
+static int rename_settings_file(const char *path, const char *new_name) {
+ _cleanup_free_ char *rs = NULL;
+ const char *fn;
+
+ fn = strjoina(new_name, ".nspawn");
+
+ rs = file_in_same_dir(path, fn);
+ if (!rs)
+ return -ENOMEM;
+
+ return rename_noreplace(AT_FDCWD, path, AT_FDCWD, rs);
}
int image_rename(Image *i, const char *new_name) {
_cleanup_release_lock_file_ LockFile global_lock = LOCK_FILE_INIT, local_lock = LOCK_FILE_INIT, name_lock = LOCK_FILE_INIT;
_cleanup_free_ char *new_path = NULL, *nn = NULL;
+ _cleanup_strv_free_ char **settings = NULL;
unsigned file_attr = 0;
+ char **j;
int r;
assert(i);
@@ -390,6 +458,10 @@ int image_rename(Image *i, const char *new_name) {
path_startswith(i->path, "/usr"))
return -EROFS;
+ settings = image_settings_path(i);
+ if (!settings)
+ return -ENOMEM;
+
/* Make sure we don't interfere with a running nspawn */
r = image_path_lock(i->path, LOCK_EX|LOCK_NB, &global_lock, &local_lock);
if (r < 0)
@@ -458,12 +530,33 @@ int image_rename(Image *i, const char *new_name) {
i->name = nn;
nn = NULL;
+ STRV_FOREACH(j, settings) {
+ r = rename_settings_file(*j, new_name);
+ if (r < 0 && r != -ENOENT)
+ log_debug_errno(r, "Failed to rename settings file %s, ignoring: %m", *j);
+ }
+
return 0;
}
+static int clone_settings_file(const char *path, const char *new_name) {
+ _cleanup_free_ char *rs = NULL;
+ const char *fn;
+
+ fn = strjoina(new_name, ".nspawn");
+
+ rs = file_in_same_dir(path, fn);
+ if (!rs)
+ return -ENOMEM;
+
+ return copy_file_atomic(path, rs, 0664, false, 0);
+}
+
int image_clone(Image *i, const char *new_name, bool read_only) {
_cleanup_release_lock_file_ LockFile name_lock = LOCK_FILE_INIT;
+ _cleanup_strv_free_ char **settings = NULL;
const char *new_path;
+ char **j;
int r;
assert(i);
@@ -471,6 +564,10 @@ int image_clone(Image *i, const char *new_name, bool read_only) {
if (!image_name_is_valid(new_name))
return -EINVAL;
+ settings = image_settings_path(i);
+ if (!settings)
+ return -ENOMEM;
+
/* Make sure nobody takes the new name, between the time we
* checked it is currently unused in all search paths, and the
* time we take possesion of it */
@@ -506,6 +603,12 @@ int image_clone(Image *i, const char *new_name, bool read_only) {
if (r < 0)
return r;
+ STRV_FOREACH(j, settings) {
+ r = clone_settings_file(*j, new_name);
+ if (r < 0 && r != -ENOENT)
+ log_debug_errno(r, "Failed to clone settings %s, ignoring: %m", *j);
+ }
+
return 0;
}
diff --git a/src/shared/pager.c b/src/shared/pager.c
index 479a9d5e8d..41da820938 100644
--- a/src/shared/pager.c
+++ b/src/shared/pager.c
@@ -38,7 +38,7 @@ static pid_t pager_pid = 0;
noreturn static void pager_fallback(void) {
int r;
- r = copy_bytes(STDIN_FILENO, STDOUT_FILENO, (off_t) -1, false);
+ r = copy_bytes(STDIN_FILENO, STDOUT_FILENO, (uint64_t) -1, false);
if (r < 0) {
log_error_errno(r, "Internal pager failed: %m");
_exit(EXIT_FAILURE);
diff --git a/src/shared/path-lookup.c b/src/shared/path-lookup.c
index f6a127174c..d803bbe07e 100644
--- a/src/shared/path-lookup.c
+++ b/src/shared/path-lookup.c
@@ -333,8 +333,7 @@ int lookup_paths_init(
log_debug("Looking for unit files in (higher priority first):\n\t%s", t);
} else {
log_debug("Ignoring unit files.");
- strv_free(p->unit_path);
- p->unit_path = NULL;
+ p->unit_path = strv_free(p->unit_path);
}
if (running_as == MANAGER_SYSTEM) {
@@ -390,8 +389,7 @@ int lookup_paths_init(
log_debug("Looking for SysV init scripts in:\n\t%s", t);
} else {
log_debug("Ignoring SysV init scripts.");
- strv_free(p->sysvinit_path);
- p->sysvinit_path = NULL;
+ p->sysvinit_path = strv_free(p->sysvinit_path);
}
if (!strv_isempty(p->sysvrcnd_path)) {
@@ -403,8 +401,7 @@ int lookup_paths_init(
log_debug("Looking for SysV rcN.d links in:\n\t%s", t);
} else {
log_debug("Ignoring SysV rcN.d links.");
- strv_free(p->sysvrcnd_path);
- p->sysvrcnd_path = NULL;
+ p->sysvrcnd_path = strv_free(p->sysvrcnd_path);
}
#else
log_debug("SysV init scripts and rcN.d links support disabled");
@@ -417,8 +414,7 @@ int lookup_paths_init(
void lookup_paths_free(LookupPaths *p) {
assert(p);
- strv_free(p->unit_path);
- p->unit_path = NULL;
+ p->unit_path = strv_free(p->unit_path);
#ifdef HAVE_SYSV_COMPAT
strv_free(p->sysvinit_path);
diff --git a/src/shared/pty.c b/src/shared/pty.c
deleted file mode 100644
index 35d9ff5f4d..0000000000
--- a/src/shared/pty.c
+++ /dev/null
@@ -1,633 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2014 David Herrmann <dh.herrmann@gmail.com>
-
- 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/>.
-***/
-
-/*
- * PTY
- * A PTY object represents a single PTY connection between a master and a
- * child. The child process is fork()ed so the caller controls what program
- * will be run.
- *
- * Programs like /bin/login tend to perform a vhangup() on their TTY
- * before running the login procedure. This also causes the pty master
- * to get a EPOLLHUP event as long as no client has the TTY opened.
- * This means, we cannot use the TTY connection as reliable way to track
- * the client. Instead, we _must_ rely on the PID of the client to track
- * them.
- * However, this has the side effect that if the client forks and the
- * parent exits, we loose them and restart the client. But this seems to
- * be the expected behavior so we implement it here.
- *
- * Unfortunately, epoll always polls for EPOLLHUP so as long as the
- * vhangup() is ongoing, we will _always_ get EPOLLHUP and cannot sleep.
- * This gets worse if the client closes the TTY but doesn't exit.
- * Therefore, the fd must be edge-triggered in the epoll-set so we
- * only get the events once they change.
- */
-
-#include <errno.h>
-#include <fcntl.h>
-#include <signal.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <sys/epoll.h>
-#include <sys/ioctl.h>
-#include <sys/uio.h>
-#include <sys/wait.h>
-#include <termios.h>
-#include <unistd.h>
-
-#include "barrier.h"
-#include "macro.h"
-#include "ring.h"
-#include "util.h"
-#include "signal-util.h"
-#include "pty.h"
-
-#define PTY_BUFSIZE 4096
-
-enum {
- PTY_ROLE_UNKNOWN,
- PTY_ROLE_PARENT,
- PTY_ROLE_CHILD,
-};
-
-struct Pty {
- unsigned long ref;
- Barrier barrier;
- int fd;
- pid_t child;
- sd_event_source *fd_source;
- sd_event_source *child_source;
-
- char in_buf[PTY_BUFSIZE];
- Ring out_buf;
-
- pty_event_t event_fn;
- void *event_fn_userdata;
-
- bool needs_requeue : 1;
- unsigned int role : 2;
-};
-
-int pty_new(Pty **out) {
- _pty_unref_ Pty *pty = NULL;
- int r;
-
- assert_return(out, -EINVAL);
-
- pty = new0(Pty, 1);
- if (!pty)
- return -ENOMEM;
-
- pty->ref = 1;
- pty->fd = -1;
- pty->barrier = (Barrier) BARRIER_NULL;
-
- pty->fd = posix_openpt(O_RDWR | O_NOCTTY | O_CLOEXEC | O_NONBLOCK);
- if (pty->fd < 0)
- return -errno;
-
- /*
- * The slave-node is initialized to uid/gid of the caller of
- * posix_openpt(). Only if devpts is mounted with fixed uid/gid this is
- * skipped. In that case, grantpt() can overwrite these, but then you
- * have to be root to use chown() (or a pt_chown helper has to be
- * present). In those cases grantpt() really does something,
- * otherwise it's a no-op. We call grantpt() here to try supporting
- * those cases, even though no-one uses that, I guess. If you need other
- * access-rights, set them yourself after this call returns (no, this is
- * not racy, it looks racy, but races regarding your own UID are never
- * important as an attacker could ptrace you; and the slave-pty is also
- * still locked).
- */
- r = grantpt(pty->fd);
- if (r < 0)
- return -errno;
-
- r = barrier_create(&pty->barrier);
- if (r < 0)
- return r;
-
- *out = pty;
- pty = NULL;
- return 0;
-}
-
-Pty *pty_ref(Pty *pty) {
- if (!pty || pty->ref < 1)
- return NULL;
-
- ++pty->ref;
- return pty;
-}
-
-Pty *pty_unref(Pty *pty) {
- if (!pty || pty->ref < 1 || --pty->ref > 0)
- return NULL;
-
- pty_close(pty);
- pty->child_source = sd_event_source_unref(pty->child_source);
- barrier_destroy(&pty->barrier);
- ring_clear(&pty->out_buf);
- free(pty);
-
- return NULL;
-}
-
-Barrier *pty_get_barrier(Pty *pty) {
- assert(pty);
- return &pty->barrier;
-}
-
-bool pty_is_unknown(Pty *pty) {
- return pty && pty->role == PTY_ROLE_UNKNOWN;
-}
-
-bool pty_is_parent(Pty *pty) {
- return pty && pty->role == PTY_ROLE_PARENT;
-}
-
-bool pty_is_child(Pty *pty) {
- return pty && pty->role == PTY_ROLE_CHILD;
-}
-
-bool pty_has_child(Pty *pty) {
- return pty_is_parent(pty) && pty->child > 0;
-}
-
-pid_t pty_get_child(Pty *pty) {
- return pty_has_child(pty) ? pty->child : -ECHILD;
-}
-
-bool pty_is_open(Pty *pty) {
- return pty && pty->fd >= 0;
-}
-
-int pty_get_fd(Pty *pty) {
- assert_return(pty, -EINVAL);
-
- return pty_is_open(pty) ? pty->fd : -EPIPE;
-}
-
-int pty_make_child(Pty *pty) {
- _cleanup_free_ char *slave_name = NULL;
- int r, fd;
-
- assert_return(pty, -EINVAL);
- assert_return(pty_is_unknown(pty), -EALREADY);
-
- r = ptsname_malloc(pty->fd, &slave_name);
- if (r < 0)
- return -errno;
-
- fd = open(slave_name, O_RDWR | O_CLOEXEC | O_NOCTTY);
- if (fd < 0)
- return -errno;
-
- safe_close(pty->fd);
- pty->fd = fd;
- pty->child = getpid();
- pty->role = PTY_ROLE_CHILD;
- barrier_set_role(&pty->barrier, BARRIER_CHILD);
-
- return 0;
-}
-
-int pty_make_parent(Pty *pty, pid_t child) {
- assert_return(pty, -EINVAL);
- assert_return(pty_is_unknown(pty), -EALREADY);
-
- pty->child = child;
- pty->role = PTY_ROLE_PARENT;
-
- return 0;
-}
-
-int pty_unlock(Pty *pty) {
- assert_return(pty, -EINVAL);
- assert_return(pty_is_unknown(pty) || pty_is_parent(pty), -EINVAL);
- assert_return(pty_is_open(pty), -ENODEV);
-
- return unlockpt(pty->fd) < 0 ? -errno : 0;
-}
-
-int pty_setup_child(Pty *pty) {
- struct termios attr;
- pid_t pid;
- int r;
-
- assert_return(pty, -EINVAL);
- assert_return(pty_is_child(pty), -EINVAL);
- assert_return(pty_is_open(pty), -EALREADY);
-
- r = reset_signal_mask();
- if (r < 0)
- return r;
-
- r = reset_all_signal_handlers();
- if (r < 0)
- return r;
-
- pid = setsid();
- if (pid < 0 && errno != EPERM)
- return -errno;
-
- r = ioctl(pty->fd, TIOCSCTTY, 0);
- if (r < 0)
- return -errno;
-
- r = tcgetattr(pty->fd, &attr);
- if (r < 0)
- return -errno;
-
- /* erase character should be normal backspace, PLEASEEE! */
- attr.c_cc[VERASE] = 010;
- /* always set UTF8 flag */
- attr.c_iflag |= IUTF8;
-
- r = tcsetattr(pty->fd, TCSANOW, &attr);
- if (r < 0)
- return -errno;
-
- if (dup2(pty->fd, STDIN_FILENO) != STDIN_FILENO ||
- dup2(pty->fd, STDOUT_FILENO) != STDOUT_FILENO ||
- dup2(pty->fd, STDERR_FILENO) != STDERR_FILENO)
- return -errno;
-
- /* only close FD if it's not a std-fd */
- pty->fd = (pty->fd > 2) ? safe_close(pty->fd) : -1;
-
- return 0;
-}
-
-void pty_close(Pty *pty) {
- if (!pty_is_open(pty))
- return;
-
- pty->fd_source = sd_event_source_unref(pty->fd_source);
- pty->fd = safe_close(pty->fd);
-}
-
-/*
- * Drain input-queue and dispatch data via the event-handler. Returns <0 on
- * error, 0 if queue is empty and 1 if we couldn't empty the input queue fast
- * enough and there's still data left.
- */
-static int pty_dispatch_read(Pty *pty) {
- unsigned int i;
- ssize_t len;
- int r;
-
- /*
- * We're edge-triggered, means we need to read the whole queue. This,
- * however, might cause us to stall if the writer is faster than we
- * are. Therefore, try reading as much as 8 times (32KiB) and only
- * bail out then.
- */
-
- for (i = 0; i < 8; ++i) {
- len = read(pty->fd, pty->in_buf, sizeof(pty->in_buf) - 1);
- if (len < 0) {
- if (errno == EINTR)
- continue;
-
- return (errno == EAGAIN) ? 0 : -errno;
- } else if (len == 0)
- continue;
-
- /* set terminating zero for debugging safety */
- pty->in_buf[len] = 0;
- r = pty->event_fn(pty, pty->event_fn_userdata, PTY_DATA, pty->in_buf, len);
- if (r < 0)
- return r;
- }
-
- /* still data left, make sure we're queued again */
- pty->needs_requeue = true;
-
- return 1;
-}
-
-/*
- * Drain output-queue by writing data to the pty. Returns <0 on error, 0 if the
- * output queue is empty now and 1 if we couldn't empty the output queue fast
- * enough and there's still data left.
- */
-static int pty_dispatch_write(Pty *pty) {
- struct iovec vec[2];
- unsigned int i;
- ssize_t len;
- size_t num;
-
- /*
- * Same as pty_dispatch_read(), we're edge-triggered so we need to call
- * write() until either all data is written or it returns EAGAIN. We
- * call it twice and if it still writes successfully, we reschedule.
- */
-
- for (i = 0; i < 2; ++i) {
- num = ring_peek(&pty->out_buf, vec);
- if (num < 1)
- return 0;
-
- len = writev(pty->fd, vec, (int)num);
- if (len < 0) {
- if (errno == EINTR)
- continue;
-
- return (errno == EAGAIN) ? 1 : -errno;
- } else if (len == 0)
- continue;
-
- ring_pull(&pty->out_buf, (size_t)len);
- }
-
- /* still data left, make sure we're queued again */
- if (ring_get_size(&pty->out_buf) > 0) {
- pty->needs_requeue = true;
- return 1;
- }
-
- return 0;
-}
-
-static int pty_fd_fn(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
- Pty *pty = userdata;
- int r_hup = 0, r_write = 0, r_read = 0, r;
-
- /*
- * Whenever we encounter I/O errors, we have to make sure to drain the
- * input queue first, before we handle any HUP. A child might send us
- * a message and immediately close the queue. We must not handle the
- * HUP first or we loose data.
- * Therefore, if we read a message successfully, we always return
- * success and wait for the next event-loop iteration. Furthermore,
- * whenever there is a write-error, we must try reading from the input
- * queue even if EPOLLIN is not set. The input might have arrived in
- * between epoll_wait() and write(). Therefore, write-errors are only
- * ever handled if the input-queue is empty. In all other cases they
- * are ignored until either reading fails or the input queue is empty.
- */
-
- if (revents & (EPOLLHUP | EPOLLERR))
- r_hup = -EPIPE;
-
- if (revents & EPOLLOUT)
- r_write = pty_dispatch_write(pty);
-
- /* Awesome! Kernel signals HUP without IN but queues are not empty.. */
- if ((revents & EPOLLIN) || r_hup < 0 || r_write < 0) {
- r_read = pty_dispatch_read(pty);
- if (r_read > 0)
- return 0; /* still data left to fetch next round */
- }
-
- if (r_hup < 0 || r_write < 0 || r_read < 0) {
- /* PTY closed and input-queue drained */
- pty_close(pty);
- r = pty->event_fn(pty, pty->event_fn_userdata, PTY_HUP, NULL, 0);
- if (r < 0)
- return r;
- }
-
- return 0;
-}
-
-static int pty_fd_prepare_fn(sd_event_source *source, void *userdata) {
- Pty *pty = userdata;
- int r;
-
- if (pty->needs_requeue) {
- /*
- * We're edge-triggered. In case we couldn't handle all events
- * or in case new write-data is queued, we set needs_requeue.
- * Before going asleep, we set the io-events *again*. sd-event
- * notices that we're edge-triggered and forwards the call to
- * the kernel even if the events didn't change. The kernel will
- * check the events and re-queue us on the ready queue in case
- * an event is pending.
- */
- r = sd_event_source_set_io_events(source, EPOLLHUP | EPOLLERR | EPOLLIN | EPOLLOUT | EPOLLET);
- if (r >= 0)
- pty->needs_requeue = false;
- }
-
- return 0;
-}
-
-static int pty_child_fn(sd_event_source *source, const siginfo_t *si, void *userdata) {
- Pty *pty = userdata;
- int r;
-
- pty->child = 0;
-
- r = pty->event_fn(pty, pty->event_fn_userdata, PTY_CHILD, si, sizeof(*si));
- if (r < 0)
- return r;
-
- return 0;
-}
-
-int pty_attach_event(Pty *pty, sd_event *event, pty_event_t event_fn, void *event_fn_userdata) {
- int r;
-
- assert_return(pty, -EINVAL);
- assert_return(event, -EINVAL);
- assert_return(event_fn, -EINVAL);
- assert_return(pty_is_parent(pty), -EINVAL);
-
- pty_detach_event(pty);
-
- if (pty_is_open(pty)) {
- r = sd_event_add_io(event,
- &pty->fd_source,
- pty->fd,
- EPOLLHUP | EPOLLERR | EPOLLIN | EPOLLOUT | EPOLLET,
- pty_fd_fn,
- pty);
- if (r < 0)
- goto error;
-
- r = sd_event_source_set_prepare(pty->fd_source, pty_fd_prepare_fn);
- if (r < 0)
- goto error;
- }
-
- if (pty_has_child(pty)) {
- r = sd_event_add_child(event,
- &pty->child_source,
- pty->child,
- WEXITED,
- pty_child_fn,
- pty);
- if (r < 0)
- goto error;
- }
-
- pty->event_fn = event_fn;
- pty->event_fn_userdata = event_fn_userdata;
-
- return 0;
-
-error:
- pty_detach_event(pty);
- return r;
-}
-
-void pty_detach_event(Pty *pty) {
- if (!pty)
- return;
-
- pty->child_source = sd_event_source_unref(pty->child_source);
- pty->fd_source = sd_event_source_unref(pty->fd_source);
- pty->event_fn = NULL;
- pty->event_fn_userdata = NULL;
-}
-
-int pty_write(Pty *pty, const void *buf, size_t size) {
- bool was_empty;
- int r;
-
- assert_return(pty, -EINVAL);
- assert_return(pty_is_open(pty), -ENODEV);
- assert_return(pty_is_parent(pty), -ENODEV);
-
- if (size < 1)
- return 0;
-
- /*
- * Push @buf[0..@size] into the output ring-buffer. In case the
- * ring-buffer wasn't empty beforehand, we're already waiting for
- * EPOLLOUT and we're done. If it was empty, we have to re-queue the
- * FD for EPOLLOUT as we're edge-triggered and wouldn't get any new
- * EPOLLOUT event.
- */
-
- was_empty = ring_get_size(&pty->out_buf) < 1;
-
- r = ring_push(&pty->out_buf, buf, size);
- if (r < 0)
- return r;
-
- if (was_empty)
- pty->needs_requeue = true;
-
- return 0;
-}
-
-int pty_signal(Pty *pty, int sig) {
- assert_return(pty, -EINVAL);
- assert_return(pty_is_open(pty), -ENODEV);
- assert_return(pty_is_parent(pty), -ENODEV);
-
- return ioctl(pty->fd, TIOCSIG, sig) < 0 ? -errno : 0;
-}
-
-int pty_resize(Pty *pty, unsigned short term_width, unsigned short term_height) {
- struct winsize ws = {
- .ws_col = term_width,
- .ws_row = term_height,
- };
-
- assert_return(pty, -EINVAL);
- assert_return(pty_is_open(pty), -ENODEV);
- assert_return(pty_is_parent(pty), -ENODEV);
-
- /*
- * This will send SIGWINCH to the pty slave foreground process group.
- * We will also get one, but we don't need it.
- */
- return ioctl(pty->fd, TIOCSWINSZ, &ws) < 0 ? -errno : 0;
-}
-
-pid_t pty_fork(Pty **out, sd_event *event, pty_event_t event_fn, void *event_fn_userdata, unsigned short initial_term_width, unsigned short initial_term_height) {
- _pty_unref_ Pty *pty = NULL;
- int r;
- pid_t pid;
-
- assert_return(out, -EINVAL);
- assert_return((event && event_fn) || (!event && !event_fn), -EINVAL);
-
- r = pty_new(&pty);
- if (r < 0)
- return r;
-
- r = pty_unlock(pty);
- if (r < 0)
- return r;
-
- pid = fork();
- if (pid < 0)
- return -errno;
-
- if (pid == 0) {
- /* child */
-
- r = pty_make_child(pty);
- if (r < 0)
- _exit(-r);
-
- r = pty_setup_child(pty);
- if (r < 0)
- _exit(-r);
-
- /* sync with parent */
- if (!barrier_place_and_sync(&pty->barrier))
- _exit(1);
-
- /* fallthrough and return the child's PTY object */
- } else {
- /* parent */
-
- r = pty_make_parent(pty, pid);
- if (r < 0)
- goto parent_error;
-
- r = pty_resize(pty, initial_term_width, initial_term_height);
- if (r < 0)
- goto parent_error;
-
- if (event) {
- r = pty_attach_event(pty, event, event_fn, event_fn_userdata);
- if (r < 0)
- goto parent_error;
- }
-
- /* sync with child */
- if (!barrier_place_and_sync(&pty->barrier)) {
- r = -ECHILD;
- goto parent_error;
- }
-
- /* fallthrough and return the parent's PTY object */
- }
-
- *out = pty;
- pty = NULL;
- return pid;
-
-parent_error:
- barrier_abort(&pty->barrier);
- waitpid(pty->child, NULL, 0);
- pty->child = 0;
- return r;
-}
diff --git a/src/shared/pty.h b/src/shared/pty.h
deleted file mode 100644
index 63c7db2833..0000000000
--- a/src/shared/pty.h
+++ /dev/null
@@ -1,72 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2014 David Herrmann <dh.herrmann@gmail.com>
-
- 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 <unistd.h>
-
-#include "barrier.h"
-#include "macro.h"
-#include "sd-event.h"
-
-typedef struct Pty Pty;
-
-enum {
- PTY_CHILD,
- PTY_HUP,
- PTY_DATA,
-};
-
-typedef int (*pty_event_t) (Pty *pty, void *userdata, unsigned int event, const void *ptr, size_t size);
-
-int pty_new(Pty **out);
-Pty *pty_ref(Pty *pty);
-Pty *pty_unref(Pty *pty);
-
-#define _pty_unref_ _cleanup_(pty_unrefp)
-DEFINE_TRIVIAL_CLEANUP_FUNC(Pty*, pty_unref);
-
-Barrier *pty_get_barrier(Pty *pty);
-
-bool pty_is_unknown(Pty *pty);
-bool pty_is_parent(Pty *pty);
-bool pty_is_child(Pty *pty);
-bool pty_has_child(Pty *pty);
-pid_t pty_get_child(Pty *pty);
-
-bool pty_is_open(Pty *pty);
-int pty_get_fd(Pty *pty);
-
-int pty_make_child(Pty *pty);
-int pty_make_parent(Pty *pty, pid_t child);
-int pty_unlock(Pty *pty);
-int pty_setup_child(Pty *pty);
-void pty_close(Pty *pty);
-
-int pty_attach_event(Pty *pty, sd_event *event, pty_event_t event_fn, void *event_fn_userdata);
-void pty_detach_event(Pty *pty);
-
-int pty_write(Pty *pty, const void *buf, size_t size);
-int pty_signal(Pty *pty, int sig);
-int pty_resize(Pty *pty, unsigned short term_width, unsigned short term_height);
-
-pid_t pty_fork(Pty **out, sd_event *event, pty_event_t event_fn, void *event_fn_userdata, unsigned short initial_term_width, unsigned short initial_term_height);