/** * vte_pty_close: * @pty: a #VtePty * * Cleans up the PTY, specifically any logging performed for the session. * The file descriptor to the PTY master remains open. */ void vte_pty_close (VtePty *pty) { #ifdef VTE_USE_GNOME_PTY_HELPER VtePtyPrivate *priv = pty->priv; gpointer tag; GnomePtyOps ops; if (!priv->using_helper) return; /* Signal the helper that it needs to close its connection. */ tag = priv->helper_tag; ops = GNOME_PTY_CLOSE_PTY; if (n_write(_vte_pty_helper_tunnel, &ops, sizeof(ops)) != sizeof(ops)) { return; } if (n_write(_vte_pty_helper_tunnel, &tag, sizeof(tag)) != sizeof(tag)) { return; } ops = GNOME_PTY_SYNCH; if (n_write(_vte_pty_helper_tunnel, &ops, sizeof(ops)) != sizeof(ops)) { return; } n_read(_vte_pty_helper_tunnel, &ops, 1); priv->helper_tag = NULL; priv->using_helper = FALSE; #endif }
int main(int argc, char **argv) { pid_t child = 0; char c; int ret; signal(SIGCHLD, sigchld_handler); fd = pty_open(&child, 0, NULL, (argc > 1) ? argv[1] : NULL, (argc > 1) ? argv + 1 : NULL, NULL, 0, 0, NULL, NULL, NULL); if (child == 0) { int i; for (i = 0; ; i++) { switch (i % 3) { case 0: case 1: fprintf(stdout, "%d\n", i); break; case 2: fprintf(stderr, "%d\n", i); break; default: g_assert_not_reached(); break; } sleep(1); } } g_print("Child pid is %d.\n", (int)child); do { ret = n_read(fd, &c, 1); if (ret == 0) { break; } if ((ret == -1) && (errno != EAGAIN) && (errno != EINTR)) { break; } if (argc < 2) { n_write(STDOUT_FILENO, "[", 1); } n_write(STDOUT_FILENO, &c, 1); if (argc < 2) { n_write(STDOUT_FILENO, "]", 1); } } while (TRUE); return 0; }
int ntttcp_server_select(struct ntttcp_stream_server *ss) { int err_code = NO_ERROR; char *log = NULL; bool verbose_log = ss->verbose; int n_fds = 0, newfd, current_fd = 0; char *buffer; //receive buffer uint64_t nbytes; //bytes read int bytes_to_be_read = 0; //read bytes from socket fd_set read_set, write_set; struct sockaddr_storage peer_addr, local_addr; //for remote peer, and local address socklen_t peer_addr_size, local_addr_size; char *ip_address_str; int ip_addr_max_size; if ( (buffer = (char *)malloc(ss->recv_buf_size)) == (char *)NULL) { PRINT_ERR("cannot allocate memory for receive buffer"); return ERROR_MEMORY_ALLOC; } ip_addr_max_size = (ss->domain == AF_INET? INET_ADDRSTRLEN : INET6_ADDRSTRLEN); if ( (ip_address_str = (char *)malloc(ip_addr_max_size)) == (char *)NULL) { PRINT_ERR("cannot allocate memory for ip address of peer"); free(buffer); return ERROR_MEMORY_ALLOC; } /* accept new client, receive data from client */ while (1) { memcpy(&read_set, &ss->read_set, sizeof(fd_set)); memcpy(&write_set, &ss->write_set, sizeof(fd_set)); /* we are notified by select() */ n_fds = select(ss->max_fd + 1, &read_set, NULL, NULL, NULL); if (n_fds < 0 && errno != EINTR) { PRINT_ERR("error happened when select()"); err_code = ERROR_SELECT; continue; } /*run through the existing connections looking for data to be read*/ for (current_fd = 0; current_fd <= ss->max_fd; current_fd++){ if ( !FD_ISSET(current_fd, &read_set) ) continue; /* then, we got one fd to hanle */ /* a NEW connection coming */ if (current_fd == ss->listener) { /* handle new connections */ peer_addr_size = sizeof(peer_addr); if ((newfd = accept(ss->listener, (struct sockaddr *) &peer_addr, &peer_addr_size)) < 0 ) { err_code = ERROR_ACCEPT; break; } /* then we got a new connection */ if (set_socket_non_blocking(newfd) == -1) { asprintf(&log, "cannot set the new socket as non-blocking: %d", newfd); PRINT_DBG_FREE(log); } FD_SET(newfd, &ss->read_set); /* add the new one to read_set */ if (newfd > ss->max_fd) { /* update the maximum */ ss->max_fd = newfd; } /* print out new connection info */ local_addr_size = sizeof(local_addr); if (getsockname(newfd, (struct sockaddr *) &local_addr, &local_addr_size) != 0) { asprintf(&log, "failed to get local address information for the new socket: %d", newfd); PRINT_DBG_FREE(log); } else{ asprintf(&log, "New connection: %s:%d --> local:%d [socket %d]", ip_address_str = retrive_ip_address_str(&peer_addr, ip_address_str, ip_addr_max_size), ntohs( ss->domain == AF_INET ? ((struct sockaddr_in *)&peer_addr)->sin_port :((struct sockaddr_in6 *)&peer_addr)->sin6_port), ntohs( ss->domain == AF_INET ? ((struct sockaddr_in *)&local_addr)->sin_port :((struct sockaddr_in6 *)&local_addr)->sin6_port), newfd); PRINT_DBG_FREE(log); } //if there is no synch thread, if any new connection coming, indicates ss started if ( ss->no_synch ) turn_on_light(); //else, leave the sync thread to fire the trigger } /* handle data from an EXISTING client */ else{ bzero(buffer, ss->recv_buf_size); bytes_to_be_read = ss->is_sync_thread ? 1 : ss->recv_buf_size; /* got error or connection closed by client */ if ((nbytes = n_read(current_fd, buffer, bytes_to_be_read)) <= 0) { if (nbytes == 0) { asprintf(&log, "socket closed: %d", current_fd); PRINT_DBG_FREE(log); } else{ asprintf(&log, "error: cannot read data from socket: %d", current_fd); PRINT_INFO_FREE(log); err_code = ERROR_NETWORK_READ; /* need to continue test and check other socket, so don't end the test */ } close(current_fd); FD_CLR(current_fd, &ss->read_set); /* remove from master set when finished */ } /* report how many bytes received */ else{ __sync_fetch_and_add(&(ss->total_bytes_transferred), nbytes); } } } } free(buffer); free(ip_address_str); close(ss->listener); return err_code; }
int ntttcp_server_epoll(struct ntttcp_stream_server *ss) { int err_code = NO_ERROR; char *log = NULL; bool verbose_log = ss->verbose; int efd = 0, n_fds = 0, newfd = 0, current_fd = 0; char *buffer; //receive buffer uint64_t nbytes; //bytes read int bytes_to_be_read = 0; //read bytes from socket struct epoll_event event, *events; struct sockaddr_storage peer_addr, local_addr; //for remote peer, and local address socklen_t peer_addr_size, local_addr_size; char *ip_address_str; int ip_addr_max_size; int i = 0; if ( (buffer = (char *)malloc(ss->recv_buf_size)) == (char *)NULL) { PRINT_ERR("cannot allocate memory for receive buffer"); return ERROR_MEMORY_ALLOC; } ip_addr_max_size = (ss->domain == AF_INET? INET_ADDRSTRLEN : INET6_ADDRSTRLEN); if ( (ip_address_str = (char *)malloc(ip_addr_max_size)) == (char *)NULL) { PRINT_ERR("cannot allocate memory for ip address of peer"); free(buffer); return ERROR_MEMORY_ALLOC; } efd = epoll_create1 (0); if (efd == -1) { PRINT_ERR("epoll_create1 failed"); free(buffer); free(ip_address_str); return ERROR_EPOLL; } event.data.fd = ss->listener; event.events = EPOLLIN; if (epoll_ctl(efd, EPOLL_CTL_ADD, ss->listener, &event) != 0) { PRINT_ERR("epoll_ctl failed"); free(buffer); free(ip_address_str); close(efd); return ERROR_EPOLL; } /* Buffer where events are returned */ events = calloc (MAX_EPOLL_EVENTS, sizeof event); while (1) { n_fds = epoll_wait (efd, events, MAX_EPOLL_EVENTS, -1); for (i = 0; i < n_fds; i++) { current_fd = events[i].data.fd; if ((events[i].events & EPOLLERR) || (events[i].events & EPOLLHUP) || (!(events[i].events & EPOLLIN))) { /* An error has occured on this fd, or the socket is not ready for reading */ PRINT_ERR("error happened on the associated connection"); close (current_fd); continue; } /* then, we got one fd to hanle */ /* a NEW connection coming */ if (current_fd == ss->listener) { /* We have a notification on the listening socket, which means one or more incoming connections. */ while (1) { peer_addr_size = sizeof (peer_addr); newfd = accept (ss->listener, (struct sockaddr *) &peer_addr, &peer_addr_size); if (newfd == -1) { if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) { /* We have processed all incoming connections. */ break; } else { PRINT_ERR("error to accept new connections"); break; } } if (set_socket_non_blocking(newfd) == -1) { asprintf(&log, "cannot set the new socket as non-blocking: %d", newfd); PRINT_DBG_FREE(log); } local_addr_size = sizeof(local_addr); if (getsockname(newfd, (struct sockaddr *) &local_addr, &local_addr_size) != 0) { asprintf(&log, "failed to get local address information for the new socket: %d", newfd); PRINT_DBG_FREE(log); } else { asprintf(&log, "New connection: %s:%d --> local:%d [socket %d]", ip_address_str = retrive_ip_address_str(&peer_addr, ip_address_str, ip_addr_max_size), ntohs( ss->domain == AF_INET ? ((struct sockaddr_in *)&peer_addr)->sin_port :((struct sockaddr_in6 *)&peer_addr)->sin6_port), ntohs( ss->domain == AF_INET ? ((struct sockaddr_in *)&local_addr)->sin_port :((struct sockaddr_in6 *)&local_addr)->sin6_port), newfd); PRINT_DBG_FREE(log); } event.data.fd = newfd; event.events = EPOLLIN; if (epoll_ctl (efd, EPOLL_CTL_ADD, newfd, &event) != 0) PRINT_ERR("epoll_ctl failed"); //if there is no synch thread, if any new connection coming, indicates ss started if ( ss->no_synch ) turn_on_light(); //else, leave the sync thread to fire the trigger } } /* handle data from an EXISTING client */ else { bzero(buffer, ss->recv_buf_size); bytes_to_be_read = ss->is_sync_thread ? 1 : ss->recv_buf_size; /* got error or connection closed by client */ if ((nbytes = n_read(current_fd, buffer, bytes_to_be_read)) <= 0) { if (nbytes == 0) { asprintf(&log, "socket closed: %d", i); PRINT_DBG_FREE(log); } else { asprintf(&log, "error: cannot read data from socket: %d", i); PRINT_INFO_FREE(log); err_code = ERROR_NETWORK_READ; /* need to continue ss and check other socket, so don't end the ss */ } close (current_fd); } /* report how many bytes received */ else { __sync_fetch_and_add(&(ss->total_bytes_transferred), nbytes); } } } } free(buffer); free(ip_address_str); free(events); close(efd); close(ss->listener); return err_code; }
/* Open the named PTY slave, fork off a child (storing its PID in child), * and exec the named command in its own session as a process group leader */ static int _pty_fork_on_pty_name(const char *path, int parent_fd, char **env_add, const char *command, char **argv, const char *directory, int columns, int rows, int *stdin_fd, int *stdout_fd, int *stderr_fd, int *held_fd, pid_t *child, gboolean reapchild, gboolean login) { int fd, hold_fd, i; char c; int ready_a[2] = { 0, 0 }; int ready_b[2] = { 0, 0 }; pid_t pid, grandchild_pid; int pid_pipe[2]; int stdin_pipe[2]; int stdout_pipe[2]; int stderr_pipe[2]; /* Optionally hold the slave PTY open in the parent. Needed to prevent * EIO from read() on the master if exec'ing a program that enumerates * and closes open fds before opening /dev/tty (ssh). Partially fixes * bug 644432. */ if (held_fd) { hold_fd = open(path, O_RDWR|O_NOCTTY); if (hold_fd == -1) { return -1; } } /* Open pipes for synchronizing between parent and child. */ if (_pty_pipe_open_bi(&ready_a[0], &ready_a[1], &ready_b[0], &ready_b[1]) == -1) { /* Error setting up pipes. Bail. */ goto bail_ready; } if (reapchild && pipe(pid_pipe)) { /* Error setting up pipes. Bail. */ goto bail_pid; } if (pipe(stdin_pipe)) { /* Error setting up pipes. Bail. */ goto bail_stdin; } if (pipe(stdout_pipe)) { /* Error setting up pipes. Bail. */ goto bail_stdout; } if (pipe(stderr_pipe)) { /* Error setting up pipes. Bail. */ goto bail_stderr; } /* Start up a child. */ pid = fork(); switch (pid) { case -1: /* Error fork()ing. Bail. */ *child = -1; return -1; break; case 0: /* Child. Close the parent's ends of the pipes. */ close(ready_a[0]); close(ready_b[1]); close(stdin_pipe[1]); close(stdout_pipe[0]); close(stderr_pipe[0]); if (held_fd) close(hold_fd); if(reapchild) { close(pid_pipe[0]); /* Fork a intermediate child. This is needed to not * produce zombies! */ grandchild_pid = fork(); if (grandchild_pid < 0) { /* Error during fork! */ n_write (pid_pipe[1], &grandchild_pid, sizeof (grandchild_pid)); _exit (1); } else if (grandchild_pid > 0) { /* Parent! (This is the actual intermediate child; * so write the pid to the parent and then exit */ n_write (pid_pipe[1], &grandchild_pid, sizeof (grandchild_pid)); close (pid_pipe[1]); _exit (0); } /* Start a new session and become process-group leader. */ setsid(); setpgid(0, 0); } /* Close most descriptors. */ for (i = 0; i < sysconf(_SC_OPEN_MAX); i++) { if ((i != ready_b[0]) && (i != ready_a[1]) && (i != stdin_pipe[0]) && (i != stdout_pipe[1]) && (i != stderr_pipe[1])) { close(i); } } /* Set up stdin/out/err */ dup2(stdin_pipe[0], STDIN_FILENO); close (stdin_pipe[0]); dup2(stdout_pipe[1], STDOUT_FILENO); close (stdout_pipe[1]); dup2(stderr_pipe[1], STDERR_FILENO); close (stderr_pipe[1]); /* Open the slave PTY, acquiring it as the controlling terminal * for this process and its children. */ fd = open(path, O_RDWR); if (fd == -1) { return -1; } #ifdef TIOCSCTTY /* TIOCSCTTY is defined? Let's try that, too. */ ioctl(fd, TIOCSCTTY, fd); #endif /* Store 0 as the "child"'s ID to indicate to the caller that * it is now the child. */ *child = 0; return _pty_run_on_pty(fd, login, stdin_pipe[1], stdout_pipe[1], stderr_pipe[1], ready_b[0], ready_a[1], env_add, command, argv, directory); break; default: /* Parent. Close the child's ends of the pipes, do the ready * handshake, and return the child's PID. */ close(ready_b[0]); close(ready_a[1]); close(stdin_pipe[0]); close(stdout_pipe[1]); close(stderr_pipe[1]); if (reapchild) { close(pid_pipe[1]); /* Reap the intermediate child */ wait_again: if (waitpid (pid, NULL, 0) < 0) { if (errno == EINTR) { goto wait_again; } else if (errno == ECHILD) { ; /* NOOP! Child already reaped. */ } else { g_warning ("waitpid() should not fail in pty-open.c"); } } /* * Read the child pid from the pid_pipe * */ if (n_read (pid_pipe[0], child, sizeof (pid_t)) != sizeof (pid_t) || *child == -1) { g_warning ("Error while spanning child!"); goto bail_fork; } close(pid_pipe[0]); } else { /* No intermediate child, simple */ *child = pid; } /* Wait for the child to be ready, set the window size, then * signal that we're ready. We need to synchronize here to * avoid possible races when the child has to do more setup * of the terminal than just opening it. */ n_read(ready_a[0], &c, 1); _pty_set_size(parent_fd, columns, rows); n_write(ready_b[1], &c, 1); close(ready_a[0]); close(ready_b[1]); *stdin_fd = stdin_pipe[1]; *stdout_fd = stdout_pipe[0]; *stderr_fd = stderr_pipe[0]; if (held_fd) *held_fd = hold_fd; return 0; break; } g_assert_not_reached(); return -1; bail_fork: close(stderr_pipe[0]); close(stderr_pipe[1]); bail_stderr: close(stdout_pipe[0]); close(stdout_pipe[1]); bail_stdout: close(stdin_pipe[0]); close(stdin_pipe[1]); bail_stdin: if(reapchild) { close(pid_pipe[0]); close(pid_pipe[1]); } bail_pid: close(ready_a[0]); close(ready_a[1]); close(ready_b[0]); close(ready_b[1]); bail_ready: *child = -1; if (held_fd) close(hold_fd); return -1; }
/* Run the given command (if specified), using the given descriptor as the * controlling terminal. */ static int _pty_run_on_pty(int fd, gboolean login, int stdin_fd, int stdout_fd, int stderr_fd, int ready_reader, int ready_writer, char **env_add, const char *command, char **argv, const char *directory) { int i; char c; char **args, *arg; #ifdef HAVE_STROPTS_H if (!ioctl (fd, I_FIND, "ptem") && ioctl (fd, I_PUSH, "ptem") == -1) { close (fd); _exit (0); return -1; } if (!ioctl (fd, I_FIND, "ldterm") && ioctl (fd, I_PUSH, "ldterm") == -1) { close (fd); _exit (0); return -1; } if (!ioctl (fd, I_FIND, "ttcompat") && ioctl (fd, I_PUSH, "ttcompat") == -1) { perror ("ioctl (fd, I_PUSH, \"ttcompat\")"); close (fd); _exit (0); return -1; } #endif /* HAVE_STROPTS_H */ /* Set any environment variables. */ for (i = 0; (env_add != NULL) && (env_add[i] != NULL); i++) { if (putenv(g_strdup(env_add[i])) != 0) { g_warning("Error adding `%s' to environment, " "continuing.", env_add[i]); } } /* Reset our signals -- our parent may have done any number of * weird things to them. */ _pty_reset_signal_handlers(); /* Change to the requested directory. */ if (directory != NULL) { i = chdir(directory); } #ifdef HAVE_UTMP_H /* This sets stdin, stdout, stderr to the socket */ if (login && login_tty (fd) == -1) { g_printerr ("mount child process login_tty failed: %s\n", g_strerror (errno)); return -1; } #endif /* Signal to the parent that we've finished setting things up by * sending an arbitrary byte over the status pipe and waiting for * a response. This synchronization step ensures that the pty is * fully initialized before the parent process attempts to do anything * with it, and is required on systems where additional setup, beyond * merely opening the device, is required. This is at least the case * on Solaris. */ /* Initialize so valgrind doesn't complain */ c = 0; n_write(ready_writer, &c, 1); fsync(ready_writer); n_read(ready_reader, &c, 1); close(ready_writer); if (ready_writer != ready_reader) { close(ready_reader); } /* If the caller provided a command, we can't go back, ever. */ if (command != NULL) { /* Outta here. */ if (argv != NULL) { for (i = 0; (argv[i] != NULL); i++) ; args = g_malloc0(sizeof(char*) * (i + 1)); for (i = 0; (argv[i] != NULL); i++) { args[i] = g_strdup(argv[i]); } execvp(command, args); } else { arg = g_strdup(command); execlp(command, arg, NULL); } /* Avoid calling any atexit() code. */ _exit(0); g_assert_not_reached(); } return 0; }
/* * _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; }