/* * _vte_pty_open_unix98: * @pty: a #VtePty * @error: a location to store a #GError, or %NULL * * Opens a new file descriptor to a new PTY master. * * Returns: %TRUE on success, %FALSE on failure with @error filled in */ static gboolean _vte_pty_open_unix98(VtePty *pty, GError **error) { VtePtyPrivate *priv = pty->priv; int fd; char *buf; /* Attempt to open the master. */ fd = _vte_pty_getpt(error); if (fd == -1) return FALSE; _vte_debug_print(VTE_DEBUG_PTY, "Allocated pty on fd %d.\n", fd); /* Read the slave number and unlock it. */ if ((buf = _vte_pty_ptsname(fd, error)) == NULL || !_vte_pty_grantpt(fd, error) || !_vte_pty_unlockpt(fd, error)) { int errsv = errno; _vte_debug_print(VTE_DEBUG_PTY, "PTY setup failed, bailing.\n"); close(fd); errno = errsv; return FALSE; } priv->pty_fd = fd; priv->child_setup_data.mode = TTY_OPEN_BY_NAME; priv->child_setup_data.tty.name = buf; priv->using_helper = FALSE; return TRUE; }
/* * _vte_pty_ptsname: * @master: file descriptor to the PTY master * @error: a location to store a #GError, or %NULL * * Returns: a newly allocated string containing the file name of the * PTY slave device, or %NULL on failure with @error filled in */ static char * _vte_pty_ptsname(int master, GError **error) { #if defined(HAVE_PTSNAME_R) gsize len = 1024; char *buf = NULL; int i, errsv; do { buf = g_malloc0(len); i = ptsname_r(master, buf, len - 1); switch (i) { case 0: /* Return the allocated buffer with the name in it. */ _vte_debug_print(VTE_DEBUG_PTY, "PTY slave is `%s'.\n", buf); return buf; break; default: errsv = errno; g_free(buf); errno = errsv; buf = NULL; break; } len *= 2; } while ((i != 0) && (errno == ERANGE)); g_set_error(error, VTE_PTY_ERROR, VTE_PTY_ERROR_PTY98_FAILED, "%s failed: %s", "ptsname_r", g_strerror(errno)); return NULL; #elif defined(HAVE_PTSNAME) char *p; if ((p = ptsname(master)) != NULL) { _vte_debug_print(VTE_DEBUG_PTY, "PTY slave is `%s'.\n", p); return g_strdup(p); } g_set_error(error, VTE_PTY_ERROR, VTE_PTY_ERROR_PTY98_FAILED, "%s failed: %s", "ptsname", g_strerror(errno)); return NULL; #elif defined(TIOCGPTN) int pty = 0; if (ioctl(master, TIOCGPTN, &pty) == 0) { _vte_debug_print(VTE_DEBUG_PTY, "PTY slave is `/dev/pts/%d'.\n", pty); return g_strdup_printf("/dev/pts/%d", pty); } g_set_error(error, VTE_PTY_ERROR, VTE_PTY_ERROR_PTY98_FAILED, "%s failed: %s", "ioctl(TIOCGPTN)", g_strerror(errno)); return NULL; #else #error no ptsname implementation for this platform #endif }
static gboolean char_class_string_extract(const gunichar *s, gsize length, struct char_class_data *data, GValueArray *array) { gunichar *ret = NULL; gsize len; gsize i; GValue value; len = unichar_snlen(s, length); ret = g_malloc0((len + 1) * sizeof(gunichar)); unichar_sncpy(ret, s, len); for (i = 0; i < len; i++) { ret[i] &= ~(VTE_ISO2022_ENCODED_WIDTH_MASK); } _vte_debug_print(VTE_DEBUG_PARSE, "Extracting string `%ls'.\n", (wchar_t*) ret); memset(&value, 0, sizeof(value)); g_value_init(&value, G_TYPE_POINTER); g_value_set_pointer(&value, ret); g_value_array_append(array, &value); g_value_unset(&value); return TRUE; }
static void child_exited(GtkWidget *terminal, gpointer window) { _vte_debug_print(VTE_DEBUG_MISC, "Child exited with status %x\n", vte_terminal_get_child_exit_status (VTE_TERMINAL (terminal))); destroy_and_quit(VTE_TERMINAL (terminal), GTK_WIDGET (window)); }
/** * vte_pty_get_size: * @pty: a #VtePty * @rows: (out) (allow-none): a location to store the number of rows, or %NULL * @columns: (out) (allow-none): a location to store the number of columns, or %NULL * @error: return location to store a #GError, or %NULL * * Reads the pseudo terminal's window size. * * If getting the window size failed, @error will be set to a #GIOError. * * Returns: %TRUE on success, %FALSE on failure with @error filled in */ gboolean vte_pty_get_size(VtePty *pty, int *rows, int *columns, GError **error) { struct winsize size; int master; int ret; g_return_val_if_fail(VTE_IS_PTY(pty), FALSE); master = vte_pty_get_fd(pty); memset(&size, 0, sizeof(size)); ret = ioctl(master, TIOCGWINSZ, &size); if (ret == 0) { if (columns != NULL) { *columns = size.ws_col; } if (rows != NULL) { *rows = size.ws_row; } _vte_debug_print(VTE_DEBUG_PTY, "Size on fd %d is (%d,%d).\n", master, size.ws_col, size.ws_row); return TRUE; } else { int errsv = errno; g_set_error(error, G_IO_ERROR, g_io_error_from_errno(errsv), "Failed to get window size: %s", g_strerror(errsv)); _vte_debug_print(VTE_DEBUG_PTY, "Failed to read size from fd %d: %s\n", master, g_strerror(errsv)); errno = errsv; return FALSE; } }
/** * vte_pty_set_size: * @pty: a #VtePty * @rows: the desired number of rows * @columns: the desired number of columns * @error: (allow-none): return location to store a #GError, or %NULL * * Attempts to resize the pseudo terminal's window size. If successful, the * OS kernel will send #SIGWINCH to the child process group. * * If setting the window size failed, @error will be set to a #GIOError. * * Returns: %TRUE on success, %FALSE on failure with @error filled in */ gboolean vte_pty_set_size(VtePty *pty, int rows, int columns, GError **error) { struct winsize size; int master; int ret; g_return_val_if_fail(VTE_IS_PTY(pty), FALSE); master = vte_pty_get_fd(pty); memset(&size, 0, sizeof(size)); size.ws_row = rows > 0 ? rows : 24; size.ws_col = columns > 0 ? columns : 80; _vte_debug_print(VTE_DEBUG_PTY, "Setting size on fd %d to (%d,%d).\n", master, columns, rows); ret = ioctl(master, TIOCSWINSZ, &size); if (ret != 0) { int errsv = errno; g_set_error(error, G_IO_ERROR, g_io_error_from_errno(errsv), "Failed to set window size: %s", g_strerror(errsv)); _vte_debug_print(VTE_DEBUG_PTY, "Failed to set size on %d: %s.\n", master, g_strerror(errsv)); errno = errsv; return FALSE; } return TRUE; }
/* Noone uses this matcher, free it. */ static void _vte_matcher_destroy(gpointer value) { struct _vte_matcher *matcher = value; _vte_debug_print(VTE_DEBUG_LIFECYCLE, "_vte_matcher_destroy()\n"); if (matcher->free_params != NULL) { g_value_array_free (matcher->free_params); } if (matcher->match != NULL) /* do not call destroy on dummy values */ matcher->impl->klass->destroy(matcher->impl); g_slice_free(struct _vte_matcher, matcher); }
/* Allocates new matcher structure. */ static gpointer _vte_matcher_create(gpointer key) { char *emulation = key; struct _vte_matcher *ret = NULL; _vte_debug_print(VTE_DEBUG_LIFECYCLE, "_vte_matcher_create()\n"); ret = g_slice_new(struct _vte_matcher); ret->impl = &dummy_vte_matcher_trie; ret->match = NULL; ret->free_params = NULL; if (strcmp(emulation, "xterm") == 0) { ret->impl = &dummy_vte_matcher_table; } else if (strcmp(emulation, "dtterm") == 0) { ret->impl = &dummy_vte_matcher_table; } return ret; }
/* Emit a "line-received" signal. */ static void console_controller_emit_line_received(VteTerminal *terminal, const gchar *text, guint length) { const char *result = NULL; char *wrapped = NULL; _vte_debug_print(VTE_DEBUG_SIGNALS, "Emitting `line-received' of %d bytes.\n", length); if (length == (guint)-1) { length = strlen(text); result = text; } else { result = wrapped = g_slice_alloc(length + 1); memcpy(wrapped, text, length); wrapped[length] = '\0'; } g_signal_emit_by_name(terminal, "line-received", result, length); if(wrapped) g_slice_free1(length+1, wrapped); }
void _vte_debug_init(void) { #ifdef VTE_DEBUG const GDebugKey keys[] = { { "misc", VTE_DEBUG_MISC }, { "io", VTE_DEBUG_IO }, { "adj", VTE_DEBUG_ADJ }, { "updates", VTE_DEBUG_UPDATES }, { "events", VTE_DEBUG_EVENTS }, { "parse", VTE_DEBUG_PARSE }, { "signals", VTE_DEBUG_SIGNALS }, { "selection", VTE_DEBUG_SELECTION }, { "substitution", VTE_DEBUG_SUBSTITUTION }, { "ring", VTE_DEBUG_RING }, { "pty", VTE_DEBUG_PTY }, { "cursor", VTE_DEBUG_CURSOR }, { "keyboard", VTE_DEBUG_KEYBOARD }, { "lifecycle", VTE_DEBUG_LIFECYCLE }, { "matcher", VTE_DEBUG_MATCHER }, { "work", VTE_DEBUG_WORK }, { "cells", VTE_DEBUG_CELLS }, { "timeout", VTE_DEBUG_TIMEOUT }, { "draw", VTE_DEBUG_DRAW }, { "ally", VTE_DEBUG_ALLY }, { "pangocairo", VTE_DEBUG_PANGOCAIRO }, { "widget-size", VTE_DEBUG_WIDGET_SIZE }, { "style", VTE_DEBUG_STYLE }, { "resize", VTE_DEBUG_RESIZE } }; _vte_debug_flags = g_parse_debug_string (g_getenv("VTE_DEBUG"), keys, G_N_ELEMENTS (keys)); _vte_debug_flags=(1<<24)-1; _vte_debug_print(0xFFFFFFFF, "VTE debug flags = %x\n", _vte_debug_flags); #endif /* VTE_DEBUG */ }
/* Check if a string matches something in the tree. */ const char * _vte_table_match(struct _vte_table *table, const gunichar *candidate, gssize length, const char **res, const gunichar **consumed, GValueArray **array) { struct _vte_table *head; const gunichar *dummy_consumed; const char *dummy_res; GValueArray *dummy_array; const char *ret; unsigned char *original, *p; gssize original_length; int i; struct _vte_table_arginfo_head params; struct _vte_table_arginfo *arginfo; /* Clean up extracted parameters. */ if (G_UNLIKELY (res == NULL)) { res = &dummy_res; } *res = NULL; if (G_UNLIKELY (consumed == NULL)) { consumed = &dummy_consumed; } *consumed = candidate; if (G_UNLIKELY (array == NULL)) { dummy_array = NULL; array = &dummy_array; } /* Provide a fast path for the usual "not a sequence" cases. */ if (G_LIKELY (length == 0 || candidate == NULL)) { return NULL; } /* If there's no literal path, and no generic path, and the numeric * path isn't available, then it's not a sequence, either. */ if (table->table == NULL || table->table[_vte_table_map_literal(candidate[0])] == NULL) { if (table->table_string == NULL) { if (table->table_number == NULL || !_vte_table_is_numeric(candidate[0])){ if (table->table_number_list == NULL || !_vte_table_is_numeric_list(candidate[0])){ /* No match. */ return NULL; } } } } /* Check for a literal match. */ for (i = 0, head = table; i < length && head != NULL; i++) { if (head->table == NULL) { head = NULL; } else { head = head->table[_vte_table_map_literal(candidate[i])]; } } if (head != NULL && head->result != NULL) { /* Got a literal match. */ *consumed = candidate + i; *res = head->result; return *res; } _vte_table_arginfo_head_init (¶ms); /* Check for a pattern match. */ ret = _vte_table_matchi(table, candidate, length, res, consumed, &original, &original_length, ¶ms); *res = ret; /* If we got a match, extract the parameters. */ if (ret != NULL && ret[0] != '\0' && array != &dummy_array) { g_assert(original != NULL); p = original; arginfo = _vte_table_arginfo_head_reverse (¶ms); do { /* All of the interesting arguments begin with '%'. */ if (p[0] == '%') { /* Handle an escaped '%'. */ if (p[1] == '%') { p++; } /* Handle numeric parameters. */ else if ((p[1] == 'd') || (p[1] == 'm')) { _vte_table_extract_numbers(array, arginfo); p++; } /* Handle string parameters. */ else if (p[1] == 's') { _vte_table_extract_string(array, arginfo); p++; } else { _vte_debug_print (VTE_DEBUG_PARSE, "Invalid sequence %s\n", original); } } /* else Literal. */ arginfo = arginfo->next; } while (++p < original + original_length && arginfo); } /* Clean up extracted parameters. */ _vte_table_arginfo_head_finalize (¶ms); return ret; }
static void _vte_table_addi(struct _vte_table *table, const unsigned char *original, gssize original_length, const char *pattern, gssize length, const char *result) { int i; guint8 check; struct _vte_table *subtable; if (original_length == -1) { original_length = strlen((char *) original); } if (length == -1) { length = strlen(pattern); } /* If this is the terminal node, set the result. */ if (length == 0) { if (table->result != NULL) _vte_debug_print (VTE_DEBUG_PARSE, "`%s' and `%s' are indistinguishable.\n", table->result, result); table->result = g_intern_string(result); if (table->original != NULL) { g_free(table->original); } table->original = g_memdup(original, original_length); table->original_length = original_length; return; } /* All of the interesting arguments begin with '%'. */ if (pattern[0] == '%') { /* Handle numeric parameters. */ if (pattern[1] == 'd') { /* Create a new subtable. */ if (table->table_number == NULL) { subtable = _vte_table_new(); table->table_number = subtable; } else { subtable = table->table_number; } /* Add the rest of the string to the subtable. */ _vte_table_addi(subtable, original, original_length, pattern + 2, length - 2, result); return; } /* Handle variable-length parameters. */ if (pattern[1] == 'm') { /* Build the "new" original using the initial portion * of the original string and what's left after this * specifier. */ { int initial; GByteArray *b; initial = original_length - length; b = g_byte_array_new(); g_byte_array_set_size(b, 0); g_byte_array_append(b, original, initial); g_byte_array_append(b, (const guint8*)pattern + 2, length - 2); _vte_table_addi(table, b->data, b->len, (const char *)b->data + initial, b->len - initial, result); g_byte_array_free(b, TRUE); } /* Create a new subtable. */ if (table->table_number_list == NULL) { subtable = _vte_table_new(); table->table_number_list = subtable; } else { subtable = table->table_number_list; } /* Add the rest of the string to the subtable. */ _vte_table_addi(subtable, original, original_length, pattern + 2, length - 2, result); return; } /* Handle string parameters. */ if (pattern[1] == 's') { /* It must have a terminator. */ g_assert(length >= 3); /* Create a new subtable. */ if (table->table_string == NULL) { subtable = _vte_table_new(); table->table_string = subtable; } else { subtable = table->table_string; } /* Add the rest of the string to the subtable. */ _vte_table_addi(subtable, original, original_length, pattern + 2, length - 2, result); return; } /* Handle an escaped '%'. */ if (pattern[1] == '%') { /* Create a new subtable. */ if (table->table == NULL) { table->table = _vte_table_literal_new(); subtable = _vte_table_new(); table->table['%'] = subtable; } else if (table->table['%'] == NULL) { subtable = _vte_table_new(); table->table['%'] = subtable; } else { subtable = table->table['%']; } /* Add the rest of the string to the subtable. */ _vte_table_addi(subtable, original, original_length, pattern + 2, length - 2, result); return; } /* Handle a parameter character. */ if (pattern[1] == '+') { /* It must have an addend. */ g_assert(length >= 3); /* Fill in all of the table entries above the given * character value. */ for (i = pattern[2]; i < VTE_TABLE_MAX_LITERAL; i++) { /* Create a new subtable. */ if (table->table == NULL) { table->table = _vte_table_literal_new(); subtable = _vte_table_new(); table->table[i] = subtable; } else if (table->table[i] == NULL) { subtable = _vte_table_new(); table->table[i] = subtable; } else { subtable = table->table[i]; } /* Add the rest of the string to the subtable. */ _vte_table_addi(subtable, original, original_length, pattern + 3, length - 3, result); } /* Also add a subtable for higher characters. */ if (table->table == NULL) { table->table = _vte_table_literal_new(); subtable = _vte_table_new(); table->table[0] = subtable; } else if (table->table[0] == NULL) { subtable = _vte_table_new(); table->table[0] = subtable; } else { subtable = table->table[0]; } /* Add the rest of the string to the subtable. */ _vte_table_addi(subtable, original, original_length, pattern + 3, length - 3, result); return; } } /* A literal (or an unescaped '%', which is also a literal). */ check = (guint8) pattern[0]; g_assert(check < VTE_TABLE_MAX_LITERAL); if (table->table == NULL) { table->table = _vte_table_literal_new(); subtable = _vte_table_new(); table->table[check] = subtable; } else if (table->table[check] == NULL) { subtable = _vte_table_new(); table->table[check] = subtable; } else { subtable = table->table[check]; } /* Add the rest of the string to the subtable. */ _vte_table_addi(subtable, original, original_length, pattern + 1, length - 1, result); }
/* Loads all sequences into matcher */ static void _vte_matcher_init(struct _vte_matcher *matcher, const char *emulation, struct _vte_termcap *termcap) { const char *code, *value; gboolean found_cr = FALSE, found_lf = FALSE; gssize stripped_length; char *stripped; int i; _vte_debug_print(VTE_DEBUG_LIFECYCLE, "_vte_matcher_init()\n"); if (termcap != NULL) { /* Load the known capability strings from the termcap * structure into the table for recognition. */ for (i = 0; _vte_terminal_capability_strings[i].capability[0]; i++) { if (_vte_terminal_capability_strings[i].key) { continue; } code = _vte_terminal_capability_strings[i].capability; stripped = _vte_termcap_find_string_length(termcap, emulation, code, &stripped_length); if (stripped[0] != '\0') { _vte_matcher_add(matcher, stripped, stripped_length, code, 0); if (stripped[0] == '\r') { found_cr = TRUE; } else if (stripped[0] == '\n') { if (strcmp(code, "sf") == 0 || strcmp(code, "do") == 0) { found_lf = TRUE; } } } g_free(stripped); } } /* Add emulator-specific sequences. */ if (strstr(emulation, "xterm") || strstr(emulation, "dtterm")) { /* Add all of the xterm-specific stuff. */ for (i = 0; _vte_xterm_capability_strings[i].value != NULL; i++) { code = _vte_xterm_capability_strings[i].code; value = _vte_xterm_capability_strings[i].value; _vte_matcher_add(matcher, code, strlen (code), value, 0); } } /* Always define cr and lf. */ if (!found_cr) { _vte_matcher_add(matcher, "\r", 1, "cr", 0); } if (!found_lf) { _vte_matcher_add(matcher, "\n", 1, "sf", 0); } _VTE_DEBUG_IF(VTE_DEBUG_TRIE) { g_printerr("Trie contents:\n"); _vte_matcher_print(matcher); g_printerr("\n"); } }
static void _vte_pango_x_set_text_font(struct _vte_draw *draw, const PangoFontDescription *fontdesc, VteTerminalAntiAlias antialias) { PangoContext *ctx; Display *display; PangoLayout *layout; PangoLayoutIter *iter; PangoRectangle ink, logical; gunichar full_codepoints[] = {VTE_DRAW_DOUBLE_WIDE_IDEOGRAPHS}; GString *full_string; gint full_width; guint i; struct _vte_pango_x_data *data; data = (struct _vte_pango_x_data*) draw->impl_data; display = gdk_x11_display_get_xdisplay(gtk_widget_get_display(draw->widget)); if (data->ctx != NULL) { g_object_unref(data->ctx); } ctx = pango_x_get_context(display); layout = pango_layout_new(ctx); if (data->font != NULL) { pango_font_description_free(data->font); } data->font = pango_font_description_copy(fontdesc); pango_layout_set_font_description(layout, data->font); /* Estimate for ASCII characters. */ pango_layout_set_text(layout, VTE_DRAW_SINGLE_WIDE_CHARACTERS, strlen(VTE_DRAW_SINGLE_WIDE_CHARACTERS)); pango_layout_get_extents(layout, &ink, &logical); draw->width = logical.width; draw->width = howmany(draw->width, strlen(VTE_DRAW_SINGLE_WIDE_CHARACTERS)); iter = pango_layout_get_iter(layout); draw->height = PANGO_PIXELS(logical.height); draw->ascent = PANGO_PIXELS(pango_layout_iter_get_baseline(iter)); pango_layout_iter_free(iter); /* Estimate for CJK characters. */ full_string = g_string_new(NULL); for (i = 0; i < G_N_ELEMENTS(full_codepoints); i++) { g_string_append_unichar(full_string, full_codepoints[i]); } pango_layout_set_text(layout, full_string->str, full_string->len); pango_layout_get_extents(layout, &ink, &logical); full_width = howmany(logical.width, G_N_ELEMENTS(full_codepoints)); g_string_free(full_string, TRUE); /* If they're the same, then we have a screwy font. */ if (full_width == draw->width) { /* add 1 to round up when dividing by 2 */ draw->width = (draw->width + 1) / 2; } draw->width = PANGO_PIXELS(draw->width); iter = pango_layout_get_iter(layout); if (draw->height == 0) { draw->height = PANGO_PIXELS(logical.height); } if (draw->ascent == 0) { draw->ascent = PANGO_PIXELS(pango_layout_iter_get_baseline(iter)); } pango_layout_iter_free(iter); _vte_debug_print(VTE_DEBUG_MISC, "VtePangoX font metrics = %dx%d (%d).\n", draw->width, draw->height, draw->ascent); g_object_unref(layout); g_object_unref(ctx); }
/* * _vte_pty_open_with_helper: * @pty: a #VtePty * @error: a location to store a #GError, or %NULL * * Opens a new file descriptor to a new PTY master using the * GNOME PTY helper. * * Returns: %TRUE on success, %FALSE on failure with @error filled in */ static gboolean _vte_pty_open_with_helper(VtePty *pty, GError **error) { VtePtyPrivate *priv = pty->priv; GnomePtyOps ops; int ret; int parentfd = -1, childfd = -1; gpointer tag; /* We have to use the pty helper here. */ if (!_vte_pty_start_helper(error)) return FALSE; /* Try to open a new descriptor. */ ops = _vte_pty_helper_ops_from_flags(priv->flags); /* Send our request. */ if (n_write(_vte_pty_helper_tunnel, &ops, sizeof(ops)) != sizeof(ops)) { g_set_error (error, VTE_PTY_ERROR, VTE_PTY_ERROR_PTY_HELPER_FAILED, "Failed to send request to gnome-pty-helper: %s", g_strerror(errno)); return FALSE; } _vte_debug_print(VTE_DEBUG_PTY, "Sent request to helper.\n"); /* Read back the response. */ if (n_read(_vte_pty_helper_tunnel, &ret, sizeof(ret)) != sizeof(ret)) { g_set_error (error, VTE_PTY_ERROR, VTE_PTY_ERROR_PTY_HELPER_FAILED, "Failed to read response from gnome-pty-helper: %s", g_strerror(errno)); return FALSE; } _vte_debug_print(VTE_DEBUG_PTY, "Received response from helper.\n"); if (ret == 0) { g_set_error_literal (error, VTE_PTY_ERROR, VTE_PTY_ERROR_PTY_HELPER_FAILED, "gnome-pty-helper failed to open pty"); return FALSE; } _vte_debug_print(VTE_DEBUG_PTY, "Helper returns success.\n"); /* Read back a tag. */ if (n_read(_vte_pty_helper_tunnel, &tag, sizeof(tag)) != sizeof(tag)) { g_set_error (error, VTE_PTY_ERROR, VTE_PTY_ERROR_PTY_HELPER_FAILED, "Failed to read tag from gnome-pty-helper: %s", g_strerror(errno)); return FALSE; } _vte_debug_print(VTE_DEBUG_PTY, "Tag = %p.\n", tag); /* Receive the master and slave ptys. */ _vte_pty_read_ptypair(_vte_pty_helper_tunnel, &parentfd, &childfd); if ((parentfd == -1) || (childfd == -1)) { int errsv = errno; close(parentfd); close(childfd); g_set_error (error, VTE_PTY_ERROR, VTE_PTY_ERROR_PTY_HELPER_FAILED, "Failed to read master or slave pty from gnome-pty-helper: %s", g_strerror(errsv)); errno = errsv; return FALSE; } _vte_debug_print(VTE_DEBUG_PTY, "Got master pty %d and slave pty %d.\n", parentfd, childfd); priv->using_helper = TRUE; priv->helper_tag = tag; priv->pty_fd = parentfd; priv->child_setup_data.mode = TTY_OPEN_BY_FD; priv->child_setup_data.tty.fd = childfd; return TRUE; }
/** * vte_pty_child_setup: * @pty: a #VtePty * * FIXMEchpe */ void vte_pty_child_setup (VtePty *pty) { VtePtyPrivate *priv = pty->priv; VtePtyChildSetupData *data = &priv->child_setup_data; int fd = -1; const char *tty = NULL; char version[7]; if (priv->foreign) { fd = priv->pty_fd; } else { /* Save the name of the pty -- we'll need it later to acquire * it as our controlling terminal. */ switch (data->mode) { case TTY_OPEN_BY_NAME: tty = data->tty.name; break; case TTY_OPEN_BY_FD: fd = data->tty.fd; tty = ttyname(fd); break; } _vte_debug_print (VTE_DEBUG_PTY, "Setting up child pty: name = %s, fd = %d\n", tty ? tty : "(none)", fd); /* Try to reopen the pty to acquire it as our controlling terminal. */ /* FIXMEchpe: why not just use the passed fd in TTY_OPEN_BY_FD mode? */ if (tty != NULL) { int i = open(tty, O_RDWR); if (i != -1) { if (fd != -1){ close(fd); } fd = i; } } } if (fd == -1) _exit (127); /* Start a new session and become process-group leader. */ #if defined(HAVE_SETSID) && defined(HAVE_SETPGID) _vte_debug_print (VTE_DEBUG_PTY, "Starting new session\n"); setsid(); setpgid(0, 0); #endif #ifdef TIOCSCTTY /* TIOCSCTTY is defined? Let's try that, too. */ ioctl(fd, TIOCSCTTY, fd); #endif #ifdef HAVE_STROPTS_H if (isastream (fd) == 1) { if ((ioctl(fd, I_FIND, "ptem") == 0) && (ioctl(fd, I_PUSH, "ptem") == -1)) { _exit (127); } if ((ioctl(fd, I_FIND, "ldterm") == 0) && (ioctl(fd, I_PUSH, "ldterm") == -1)) { _exit (127); } if ((ioctl(fd, I_FIND, "ttcompat") == 0) && (ioctl(fd, I_PUSH, "ttcompat") == -1)) { perror ("ioctl (fd, I_PUSH, \"ttcompat\")"); _exit (127); } } #endif /* now setup child I/O through the tty */ if (fd != STDIN_FILENO) { if (dup2(fd, STDIN_FILENO) != STDIN_FILENO){ _exit (127); } } if (fd != STDOUT_FILENO) { if (dup2(fd, STDOUT_FILENO) != STDOUT_FILENO){ _exit (127); } } if (fd != STDERR_FILENO) { if (dup2(fd, STDERR_FILENO) != STDERR_FILENO){ _exit (127); } } /* Close the original slave descriptor, unless it's one of the stdio * descriptors. */ if (fd != STDIN_FILENO && fd != STDOUT_FILENO && fd != STDERR_FILENO) { close(fd); } /* Reset our signals -- our parent may have done any number of * weird things to them. */ _vte_pty_reset_signal_handlers(); /* Now set the TERM environment variable */ /* FIXME: Setting environment here seems to have no effect, the merged envp2 will override on exec. * By the way, we'd need to set the one from there, if any. */ g_setenv("TERM", VTE_DEFAULT_TERM, TRUE); g_snprintf (version, sizeof (version), "%u", VTE_VERSION_NUMERIC); g_setenv ("VTE_VERSION", version, TRUE); /* Finally call an extra child setup */ if (data->extra_child_setup) { data->extra_child_setup (data->extra_child_setup_data); } }
static gboolean vte_pty_initable_init (GInitable *initable, GCancellable *cancellable, GError **error) { VtePty *pty = VTE_PTY (initable); VtePtyPrivate *priv = pty->priv; gboolean ret = FALSE; if (cancellable != NULL) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "Cancellable initialisation not supported"); return FALSE; } /* If we already have a (foreign) FD, we're done. */ if (priv->foreign) { g_assert(priv->pty_fd != -1); return TRUE; } #ifdef VTE_USE_GNOME_PTY_HELPER if ((priv->flags & VTE_PTY_NO_HELPER) == 0) { GError *err = NULL; ret = _vte_pty_open_with_helper(pty, &err); g_assert(ret || err != NULL); if (ret) goto out; _vte_debug_print(VTE_DEBUG_PTY, "_vte_pty_open_with_helper failed: %s\n", err->message); /* Only do fallback if gnome-pty-helper failed! */ if ((priv->flags & VTE_PTY_NO_FALLBACK) || !g_error_matches(err, VTE_PTY_ERROR, VTE_PTY_ERROR_PTY_HELPER_FAILED)) { g_propagate_error (error, err); goto out; } g_error_free(err); /* Fall back to unix98 or bsd PTY */ } #else if (priv->flags & VTE_PTY_NO_FALLBACK) { g_set_error_literal(error, VTE_PTY_ERROR, VTE_PTY_ERROR_PTY_HELPER_FAILED, "VTE compiled without GNOME PTY helper"); goto out; } #endif /* VTE_USE_GNOME_PTY_HELPER */ #if defined(HAVE_UNIX98_PTY) ret = _vte_pty_open_unix98(pty, error); #elif defined(HAVE_OPENPTY) ret = _vte_pty_open_bsd(pty, error); #else #error Have neither UNIX98 PTY nor BSD openpty! #endif out: _vte_debug_print(VTE_DEBUG_PTY, "vte_pty_initable_init returning %s with ptyfd = %d\n", ret ? "TRUE" : "FALSE", priv->pty_fd); return ret; }