Race condition with PTY


 
Thread Tools Search this Thread
Top Forums Programming Race condition with PTY
# 1  
Old 11-10-2009
Race condition with PTY

I've been experimenting with pseudo-terminals and found something I don't quite understand. Writing an EOF character to the master end doesn't work quite as I expect. Once I've written any other data, the master pty seems to treat a single ^D as a seperator, i.e. writing "abcabc" would let cat do one read of six bytes, but "abc\x04abc" would make cat two reads of three bytes. Is this behavior documented anywhere?

Here's the code I test with. I asserted everything in case there were errors I was missing but didn't find any...
Code:
#include <unistd.h>
#include <fcntl.h>
#include <pty.h>
#include <errno.h>
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <assert.h>

// My system doesn't declare ptsname for some reason
// but it clearly exists.
#define _XOPEN_SOURCE
#include <stdlib.h>
extern char *ptsname(int fd);


#include <sys/types.h>
#include <sys/wait.h>


/**
 *	popen() and system() create a whole new shell, which
 * 	clutters strace output with tons of garbage.
 *	this just exec's raw and does no such thing.
 */
int system_noshell(const char *cmd, ...);

int main(int argc, char *argv[])
{	// Open a pseudo-terminal, but don't make it our stdin/out/err
	int fd_master=posix_openpt(O_RDWR|O_NOCTTY);
	int ppid=getpid();	// Process ID of parent
	int cpid=-1;		// Get child PID later
	const char *pts;	// slave pseudoterm

	int s;	
	char c;

	assert( fd_master >= 0 );	// Did master terminal open?

	fprintf(stderr, "[P] Opened fd_master = %d\n", fd_master);

	assert( unlockpt(fd_master) >= 0 );	// Unlock PTY

	fprintf(stderr, "[P] Unlocked fd_master\n");

	assert( (pts=ptsname(fd_master)) != NULL ); // Get slave name

	fprintf(stderr, "[P] Name of slave: %s\n", pts);

	// Create a child process to use the slave pty
	assert( (cpid=fork()) >= 0);

	/**
	 *	Following a specific series of events here:
	 *	Parent			Child
	 *  ===================================
	 *	Open Master
	 *	fork
	 *				Open Child
	 *				Close Master
	 *	read			write '|'
	 *	write 'abc'
	 *	write EOF EOF
	 *				read abc
	 *				EOF
	 *				exit
	 *	wait()
	 *	exit
	 */

	if(cpid == 0)	// Child code
	{
		int fd_slave=-1;	// Slave PTY
		// Save real stderr so fprintf writes to it instead of pty
		int STDERR=dup(STDERR_FILENO);
		FILE *stderr=fdopen(STDERR, "w");
		setvbuf(stderr, NULL, _IONBF, 0);

		assert( close(fd_master) >= 0 ); // Ditch master PTY

		assert( (fd_slave=open(pts,O_RDWR)) >= 0); // Open slave PTY

		fprintf(stderr, "\t[C] Opened slave %s\n", pts);

		// This will dup fd over stdin,out,err then close fd
		// This function needs compilation with -lutil
		assert( login_tty(fd_slave) >= 0 );

                // We can't use assert after here, it'll print to the real stderr
                if(system_noshell("/bin/stty", "-echo", NULL) != 0)
                {
                        fprintf(stderr, "\t[C] Couldn't disable echo\n");
                        exit(1);
                }

		fprintf(stderr, "\t[C] Disabled echo\n");

		// We let the parent know the child has control of
		// the slave terminal by writing a char to it.
		if(write(STDOUT_FILENO, "|", 1) != 1 )
                {
                        fprintf(stderr, "\t[C] Couldn't send |\n");
                        exit(1);
                }

		fprintf(stderr, "\t[C] Wrote char\n");

		// We exec cat, to read until EOF.
		execl("/bin/cat", "/bin/cat", NULL);

                fprintf(stderr, \t[C] Couldn't exec: %s\n",
                         strerror(errno));
		exit(1);
	}

	fprintf(stderr, "[P] Created child pid=%d\n", cpid);

	// Parent code
	assert( read(fd_master, &c, 1) == 1 );
	assert( c == '|' );	// Child should have written |

	fprintf(stderr, "[P] Read first char from child\n");

	// Write "abc" to master end of terminal
	assert( write(fd_master, "abc", 3) == 3 );

	fprintf(stderr, "[P] Wrote data to child\n");

	// The child hangs if I don't write two EOF chars.
	assert( write(fd_master, "\x04\x04", 2) == 2 );

	fprintf(stderr, "[P] Wrote EOF\n");

	while(read(fd_master, &c, 1) == 1)	// Read bytes until EOF
	{
		if(c<0x20)	fprintf(stderr, "[P] Read ^%c\n", c+'@');
		else		fprintf(stderr, "[P] Read '%c'\n", c);
	}

	assert( wait(&s) == cpid );	// Wait for child to exit
	fprintf(stderr, "[P] Child has exited\n");
	assert( close(fd_master) >= 0 );
	fprintf(stderr, "[P] All finished.\n");
	return(0);
}

#define MAX_ARGS 16

int system_noshell(const char *cmd, ...)
{
	const char *args[MAX_ARGS]={cmd};
	int n=0, status;
	va_list ap;

	// Assemble varargs into the args array
	va_start(ap, cmd);
		do
			args[++n]=va_arg(ap, void *);
		while((args[n]) && (n<(MAX_ARGS-2)));
	va_end(ap);

	args[++n]=NULL;	// Terminate argument list

	n=fork();
	if(n < 0)
		return(-1);
	else if(n == 0)
	{
		execvp(cmd, args);
		exit(255);
	}

	assert( waitpid(n, &status, 0) == n);
	return(WEXITSTATUS(status));
}


Last edited by Corona688; 11-10-2009 at 04:27 PM.. Reason: removed some bad asserts
# 2  
Old 11-11-2009
Seems it is documented behavior...see this link which explains why a trailing / embedded EOF acts as a line delimiter.
This User Gave Thanks to shamrock For This Post:
Login or Register to Ask a Question

Previous Thread | Next Thread

8 More Discussions You Might Find Interesting

1. Programming

problem about race condition

Hi all, i'm reading Andrew S.Tanenbaum's book --- Modern Operating System.At the part of discussing race condition.And the author gives a solution with using the TSL instruction,say that one process must call the enter_region function before entering the critical regions and call the leave_region... (0 Replies)
Discussion started by: homeboy
0 Replies

2. Programming

In unix how we can test or check race condition in a c program by using multi threads

In unix how we can test or check race condition in any c program by using multi thread programming (1 Reply)
Discussion started by: afroze
1 Replies

3. Programming

In unix how we can test or check race condition in c program by using multi threads

In unix how we can test or check race condition in any c program by using multi thread programming (5 Replies)
Discussion started by: afroze
5 Replies

4. UNIX for Dummies Questions & Answers

In unix how we can test or check race condition in a c program by using multi threads

In unix how we can test or check race condition in any c program by using multi thread programming (1 Reply)
Discussion started by: afroze
1 Replies

5. Linux

In unix how we can test or check race condition in c program by using multi threads

In unix how we can test or check race condition in any c program by using multi thread programming (1 Reply)
Discussion started by: afroze
1 Replies

6. Shell Programming and Scripting

race condition with wait() function

Hi, I'm currently writing a bash script, that starts multiple threads: ____________________ #!/bin/bash loop=0 while((loop!=10)) do thread & ((loop++)) done #wait for all sub-processes (thread) to finish wait ___________________ Now I want to know, what happens, if a... (2 Replies)
Discussion started by: tho99
2 Replies

7. UNIX for Dummies Questions & Answers

use of tty and pty files

Hi, According to my understanding tty files that are available in /dev directory are terminals that are given to different users. please help me understand what are /pty files, like are they drivers to the devices.. also is the default tty terminal given to a user.. (2 Replies)
Discussion started by: saharookiedba
2 Replies

8. AIX

How to monitor pty

Hi all, today I could not telnet in AIX 5.2 cause I received the error "telnetd: All network ports in use". To allow users to telnet again I increased the number of ptys from default 256 to the new number 512. To avoid the same problem in the future and for a better understanding, I need... (2 Replies)
Discussion started by: l-roner
2 Replies
Login or Register to Ask a Question