Skip to content

Instantly share code, notes, and snippets.

@zed
Last active September 10, 2022 17:42
Show Gist options
  • Save zed/71bc51d8968dc04f44076596ec5f73ef to your computer and use it in GitHub Desktop.
Save zed/71bc51d8968dc04f44076596ec5f73ef to your computer and use it in GitHub Desktop.
setting EXTPROC on the master side works on Linux
/** from @Hans Lub's answer
https://stackoverflow.com/questions/21641754/when-pty-pseudo-terminal-slave-fd-settings-are-changed-by-tcsetattr-how-ca
Demo program for managing a pty in packet mode with the slave's
** EXTPROC bit set, where the master gets notified of changes in the
** slaves terminal attributes
**
** save as extproc.c, compile with gcc -o extproc extproc.c -lutil
*/
#include <stdio.h>
#include <pty.h>
#include <termios.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/select.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
#define BUFSIZE 512
void main() {
int master; // fd of master side
pid_t pid;
if((pid = forkpty(&master, NULL, NULL, NULL))) { // we're parent
fd_set rfds, xfds;
int retval, nread, status = 0, nonzero = 1;
char buf[BUFSIZE];
ioctl(master, TIOCPKT, &nonzero); // initiate packet mode - necessary to get notified of
// ioctl() on the slave side
struct termios tio;
// First set the EXTPROC bit in the XXX *master* end termios structure
tcgetattr(master, &tio);
tio.c_lflag |= EXTPROC;
tcsetattr(master, TCSANOW, &tio);
while(1) {
// prepare the file descriptor sets
FD_ZERO(&rfds);
FD_SET(master, &rfds);
FD_ZERO(&xfds);
FD_SET(master, &xfds);
// now wait until status of master changes
printf("---- waiting for something to happen -----\n");
select(1 + master, &rfds, NULL, &xfds, NULL);
char *r_text = (FD_ISSET(master, &rfds) ? "master ready for reading" : "- ");
char *x_text = (FD_ISSET(master, &xfds) ? "exception on master" : "- ");
printf("rfds: %s, xfds: %s\n", r_text, x_text);
if ((nread = read(master, buf, BUFSIZE-1)) < 0)
perror("read error");
else {
buf[nread] = '\0';
// In packet mode *buf will be the status byte , and buf + 1 the "payload"
char *pkt_txt = (*buf & TIOCPKT_IOCTL ? " (TIOCPKT_IOCTL)" : "");
printf("read %d bytes: status byte %x%s, payload <%s>\n", nread, *buf, pkt_txt, buf + 1);
}
if (waitpid(pid, &status, WNOHANG) && WIFEXITED(status)) {
printf("child exited with status %x\n", status);
exit(EXIT_SUCCESS);
}
}
} else { // child
// Wait a bit and do an ordinary write()
sleep(1);
write(STDOUT_FILENO,"blah", 4);
// Wait a bit and change the pty terminal attributes. This will be picked up by the master end
sleep(1);
struct termios tio;
tcgetattr(STDIN_FILENO, &tio);
tio.c_cc[VINTR] = 0x07;
tcsetattr(STDIN_FILENO, TCSANOW, &tio);
// Wait a bit and exit
sleep(1);
}
}
NAME := $(patsubst %.c,%,$(wildcard *.c))
LDLIBS= -lutil
run: $(NAME)
./$(NAME)
@ranshalit
Copy link

Awesome, I wander how you knew how to do this, I haven't find any information on this.
Can pty be used if we have serial in userspace and want to treat it as a regular linux device in /dev ? (which can be opened and set baudread, and read/write to) , thanks,.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment