summaryrefslogtreecommitdiff
path: root/src/shared
diff options
context:
space:
mode:
Diffstat (limited to 'src/shared')
-rw-r--r--src/shared/ptyfwd.c102
-rw-r--r--src/shared/ptyfwd.h8
2 files changed, 77 insertions, 33 deletions
diff --git a/src/shared/ptyfwd.c b/src/shared/ptyfwd.c
index 02c03b98d8..24055e772b 100644
--- a/src/shared/ptyfwd.c
+++ b/src/shared/ptyfwd.c
@@ -68,6 +68,8 @@ struct PTYForward {
bool read_from_master:1;
+ bool done:1;
+
bool last_char_set:1;
char last_char;
@@ -76,10 +78,54 @@ struct PTYForward {
usec_t escape_timestamp;
unsigned escape_counter;
+
+ PTYForwardHandler handler;
+ void *userdata;
};
#define ESCAPE_USEC (1*USEC_PER_SEC)
+static void pty_forward_disconnect(PTYForward *f) {
+
+ if (f) {
+ f->stdin_event_source = sd_event_source_unref(f->stdin_event_source);
+ f->stdout_event_source = sd_event_source_unref(f->stdout_event_source);
+
+ f->master_event_source = sd_event_source_unref(f->master_event_source);
+ f->sigwinch_event_source = sd_event_source_unref(f->sigwinch_event_source);
+ f->event = sd_event_unref(f->event);
+
+ if (f->saved_stdout)
+ tcsetattr(STDOUT_FILENO, TCSANOW, &f->saved_stdout_attr);
+ if (f->saved_stdin)
+ tcsetattr(STDIN_FILENO, TCSANOW, &f->saved_stdin_attr);
+
+ f->saved_stdout = f->saved_stdin = false;
+ }
+
+ /* STDIN/STDOUT should not be nonblocking normally, so let's unconditionally reset it */
+ fd_nonblock(STDIN_FILENO, false);
+ fd_nonblock(STDOUT_FILENO, false);
+}
+
+static int pty_forward_done(PTYForward *f, int rcode) {
+ _cleanup_(sd_event_unrefp) sd_event *e = NULL;
+ assert(f);
+
+ if (f->done)
+ return 0;
+
+ e = sd_event_ref(f->event);
+
+ f->done = true;
+ pty_forward_disconnect(f);
+
+ if (f->handler)
+ return f->handler(f, rcode, f->userdata);
+ else
+ return sd_event_exit(e, rcode < 0 ? EXIT_FAILURE : rcode);
+}
+
static bool look_for_escape(PTYForward *f, const char *buffer, size_t n) {
const char *p;
@@ -147,7 +193,7 @@ static int shovel(PTYForward *f) {
f->stdin_event_source = sd_event_source_unref(f->stdin_event_source);
} else {
log_error_errno(errno, "read(): %m");
- return sd_event_exit(f->event, EXIT_FAILURE);
+ return pty_forward_done(f, -errno);
}
} else if (k == 0) {
/* EOF on stdin */
@@ -156,12 +202,10 @@ static int shovel(PTYForward *f) {
f->stdin_event_source = sd_event_source_unref(f->stdin_event_source);
} else {
- /* Check if ^] has been
- * pressed three times within
- * one second. If we get this
- * we quite immediately. */
+ /* Check if ^] has been pressed three times within one second. If we get this we quite
+ * immediately. */
if (look_for_escape(f, f->in_buffer + f->in_buffer_full, k))
- return sd_event_exit(f->event, EXIT_FAILURE);
+ return pty_forward_done(f, -ECANCELED);
f->in_buffer_full += (size_t) k;
}
@@ -181,7 +225,7 @@ static int shovel(PTYForward *f) {
f->master_event_source = sd_event_source_unref(f->master_event_source);
} else {
log_error_errno(errno, "write(): %m");
- return sd_event_exit(f->event, EXIT_FAILURE);
+ return pty_forward_done(f, -errno);
}
} else {
assert(f->in_buffer_full >= (size_t) k);
@@ -211,7 +255,7 @@ static int shovel(PTYForward *f) {
f->master_event_source = sd_event_source_unref(f->master_event_source);
} else {
log_error_errno(errno, "read(): %m");
- return sd_event_exit(f->event, EXIT_FAILURE);
+ return pty_forward_done(f, -errno);
}
} else {
f->read_from_master = true;
@@ -232,7 +276,7 @@ static int shovel(PTYForward *f) {
f->stdout_event_source = sd_event_source_unref(f->stdout_event_source);
} else {
log_error_errno(errno, "write(): %m");
- return sd_event_exit(f->event, EXIT_FAILURE);
+ return pty_forward_done(f, -errno);
}
} else {
@@ -255,7 +299,7 @@ static int shovel(PTYForward *f) {
if ((f->out_buffer_full <= 0 || f->stdout_hangup) &&
(f->in_buffer_full <= 0 || f->master_hangup))
- return sd_event_exit(f->event, EXIT_SUCCESS);
+ return pty_forward_done(f, 0);
}
return 0;
@@ -418,27 +462,8 @@ int pty_forward_new(
}
PTYForward *pty_forward_free(PTYForward *f) {
-
- if (f) {
- sd_event_source_unref(f->stdin_event_source);
- sd_event_source_unref(f->stdout_event_source);
- sd_event_source_unref(f->master_event_source);
- sd_event_source_unref(f->sigwinch_event_source);
- sd_event_unref(f->event);
-
- if (f->saved_stdout)
- tcsetattr(STDOUT_FILENO, TCSANOW, &f->saved_stdout_attr);
- if (f->saved_stdin)
- tcsetattr(STDIN_FILENO, TCSANOW, &f->saved_stdin_attr);
-
- free(f);
- }
-
- /* STDIN/STDOUT should not be nonblocking normally, so let's
- * unconditionally reset it */
- fd_nonblock(STDIN_FILENO, false);
- fd_nonblock(STDOUT_FILENO, false);
-
+ pty_forward_disconnect(f);
+ free(f);
return NULL;
}
@@ -477,8 +502,21 @@ int pty_forward_set_ignore_vhangup(PTYForward *f, bool b) {
return 0;
}
-int pty_forward_get_ignore_vhangup(PTYForward *f) {
+bool pty_forward_get_ignore_vhangup(PTYForward *f) {
assert(f);
return !!(f->flags & PTY_FORWARD_IGNORE_VHANGUP);
}
+
+bool pty_forward_is_done(PTYForward *f) {
+ assert(f);
+
+ return f->done;
+}
+
+void pty_forward_set_handler(PTYForward *f, PTYForwardHandler cb, void *userdata) {
+ assert(f);
+
+ f->handler = cb;
+ f->userdata = userdata;
+}
diff --git a/src/shared/ptyfwd.h b/src/shared/ptyfwd.h
index a046eb4e5e..bd5d5fec0d 100644
--- a/src/shared/ptyfwd.h
+++ b/src/shared/ptyfwd.h
@@ -37,12 +37,18 @@ typedef enum PTYForwardFlags {
PTY_FORWARD_IGNORE_INITIAL_VHANGUP = 4,
} PTYForwardFlags;
+typedef int (*PTYForwardHandler)(PTYForward *f, int rcode, void*userdata);
+
int pty_forward_new(sd_event *event, int master, PTYForwardFlags flags, PTYForward **f);
PTYForward *pty_forward_free(PTYForward *f);
int pty_forward_get_last_char(PTYForward *f, char *ch);
int pty_forward_set_ignore_vhangup(PTYForward *f, bool ignore_vhangup);
-int pty_forward_get_ignore_vhangup(PTYForward *f);
+bool pty_forward_get_ignore_vhangup(PTYForward *f);
+
+bool pty_forward_is_done(PTYForward *f);
+
+void pty_forward_set_handler(PTYForward *f, PTYForwardHandler handler, void *userdata);
DEFINE_TRIVIAL_CLEANUP_FUNC(PTYForward*, pty_forward_free);