1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22 /*
23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <errno.h>
31 #include <signal.h>
32 #include <locale.h>
33
34 #include <unistd.h>
35 #include <termios.h>
36
37 #ifdef HAVE_SELECT
38 #ifdef HAVE_SYS_SELECT_H
39 #include <sys/select.h>
40 #endif
41 #endif
42
43 #include <fcntl.h>
44 #include <sys/time.h>
45 #include <sys/types.h>
46 #include <sys/wait.h>
47 #include <dirent.h>
48
49 #if HAVE_SYSV_PTY
50 #include <stropts.h> /* System-V stream I/O */
51 char *ptsname(int fd);
52 int grantpt(int fd);
53 int unlockpt(int fd);
54 #endif
55
56 #include "libtecla.h"
57
58 /*
59 * Pseudo-terminal devices are found in the following directory.
60 */
61 #define PTY_DEV_DIR "/dev/"
62
63 /*
64 * Pseudo-terminal controller device file names start with the following
65 * prefix.
66 */
67 #define PTY_CNTRL "pty"
68
69 /*
70 * Pseudo-terminal subsidiary device file names start with the following
71 * prefix.
72 */
73 #define PTY_SUBSID "tty"
74
75 /*
76 * Specify the maximum suffix length for the control and subsidiary device
77 * names.
78 */
79 #define PTY_MAX_SUFFIX 10
80
81 /*
82 * Set the maximum length of the manager and subsidiary terminal device
83 * filenames, including space for a terminating '\0'.
84 */
85 #define PTY_MAX_NAME (sizeof(PTY_DEV_DIR)-1 + \
86 (sizeof(PTY_SUBSID) > sizeof(PTY_CNTRL) ? \
87 sizeof(PTY_SUBSID) : sizeof(PTY_CNTRL))-1 \
88 + PTY_MAX_SUFFIX + 1)
89 /*
90 * Set the maximum length of an input line.
91 */
92 #define PTY_MAX_LINE 4096
93
94 /*
95 * Set the size of the buffer used for accumulating bytes written by the
96 * user's terminal to its stdout.
97 */
98 #define PTY_MAX_READ 1000
99
100 /*
101 * Set the amount of memory used to record history.
102 */
103 #define PTY_HIST_SIZE 10000
104
105 /*
106 * Set the timeout delay used to check for quickly arriving
107 * sequential output from the application.
108 */
109 #define PTY_READ_TIMEOUT 100000 /* micro-seconds */
110
111 static int pty_open_manager(const char *prog, int *cntrl, char *subsid_name);
112 static int pty_open_subsid(const char *prog, char *subsid_name);
113 static int pty_child(const char *prog, int subsid, char *argv[]);
114 static int pty_parent(const char *prog, int cntrl);
115 static int pty_stop_parent(int waserr, int cntrl, GetLine *gl, char *rbuff);
116 static GL_FD_EVENT_FN(pty_read_from_program);
117 static int pty_write_to_fd(int fd, const char *string, int n);
118 static void pty_child_exited(int sig);
119 static int pty_manager_readable(int fd, long usec);
120
121 /*.......................................................................
122 * Run a program with enhanced terminal editing facilities.
123 *
124 * Usage:
125 * enhance program [args...]
126 */
main(int argc,char * argv[])127 int main(int argc, char *argv[])
128 {
129 int cntrl = -1; /* The fd of the pseudo-terminal controller device */
130 int subsid = -1; /* The fd of the pseudo-terminal subsidiary device */
131 pid_t pid; /* The return value of fork() */
132 int status; /* The return statuses of the parent and child functions */
133 char subsid_name[PTY_MAX_NAME]; /* The filename of the subsidiary end of */
134 /* the pseudo-terminal. */
135 char *prog; /* The name of the program (ie. argv[0]) */
136 /*
137 * Check the arguments.
138 */
139 if(argc < 2) {
140 fprintf(stderr, "Usage: %s <program> [arguments...]\n", argv[0]);
141 return 1;
142 };
143 /*
144 * Get the name of the program.
145 */
146 prog = argv[0];
147 /*
148 * If the user has the LC_CTYPE or LC_ALL environment variables set,
149 * enable display of characters corresponding to the specified locale.
150 */
151 (void) setlocale(LC_CTYPE, "");
152 /*
153 * If the program is taking its input from a pipe or a file, or
154 * sending its output to something other than a terminal, run the
155 * program without tecla.
156 */
157 if(!isatty(STDIN_FILENO) || !isatty(STDOUT_FILENO)) {
158 if(execvp(argv[1], argv + 1) < 0) {
159 fprintf(stderr, "%s: Unable to execute %s (%s).\n", prog, argv[1],
160 strerror(errno));
161 fflush(stderr);
162 _exit(1);
163 };
164 };
165 /*
166 * Open the manager side of a pseudo-terminal pair, and return
167 * the corresponding file descriptor and the filename of the
168 * subsidiary end of the pseudo-terminal.
169 */
170 if(pty_open_manager(prog, &cntrl, subsid_name))
171 return 1;
172 /*
173 * Set up a signal handler to watch for the child process exiting.
174 */
175 signal(SIGCHLD, pty_child_exited);
176 /*
177 * The above signal handler sends the parent process a SIGINT signal.
178 * This signal is caught by gl_get_line(), which resets the terminal
179 * settings, and if the application signal handler for this signal
180 * doesn't abort the process, gl_get_line() returns NULL with errno
181 * set to EINTR. Arrange to ignore the signal, so that gl_get_line()
182 * returns and we have a chance to cleanup.
183 */
184 signal(SIGINT, SIG_IGN);
185 /*
186 * We will read user input in one process, and run the user's program
187 * in a child process.
188 */
189 pid = fork();
190 if(pid < 0) {
191 fprintf(stderr, "%s: Unable to fork child process (%s).\n", prog,
192 strerror(errno));
193 return 1;
194 };
195 /*
196 * Are we the parent?
197 */
198 if(pid!=0) {
199 status = pty_parent(prog, cntrl);
200 close(cntrl);
201 } else {
202 close(cntrl); /* The child doesn't use the subsidiary device */
203 signal(SIGCHLD, pty_child_exited);
204 if((subsid = pty_open_subsid(prog, subsid_name)) >= 0) {
205 status = pty_child(prog, subsid, argv + 1);
206 close(subsid);
207 } else {
208 status = 1;
209 };
210 };
211 return status;
212 }
213
214 /*.......................................................................
215 * Open the manager side of a pseudo-terminal pair, and return
216 * the corresponding file descriptor and the filename of the
217 * subsidiary end of the pseudo-terminal.
218 *
219 * Input/Output:
220 * prog const char * The name of this program.
221 * cntrl int * The file descriptor of the pseudo-terminal
222 * controller device will be assigned tp *cntrl.
223 * subsid_name char * The file-name of the pseudo-terminal subsidiary device
224 * will be recorded in subsid_name[], which must have
225 * at least PTY_MAX_NAME elements.
226 * Output:
227 * return int 0 - OK.
228 * 1 - Error.
229 */
pty_open_manager(const char * prog,int * cntrl,char * subsid_name)230 static int pty_open_manager(const char *prog, int *cntrl, char *subsid_name)
231 {
232 char manager_name[PTY_MAX_NAME]; /* The filename of the manager device */
233 DIR *dir; /* The directory iterator */
234 struct dirent *file; /* A file in "/dev" */
235 /*
236 * Mark the controller device as not opened yet.
237 */
238 *cntrl = -1;
239 /*
240 * On systems with the Sys-V pseudo-terminal interface, we don't
241 * have to search for a free manager terminal. We just open /dev/ptmx,
242 * and if there is a free manager terminal device, we are given a file
243 * descriptor connected to it.
244 */
245 #if HAVE_SYSV_PTY
246 *cntrl = open("/dev/ptmx", O_RDWR);
247 if(*cntrl >= 0) {
248 /*
249 * Get the filename of the subsidiary side of the pseudo-terminal.
250 */
251 char *name = ptsname(*cntrl);
252 if(name) {
253 if(strlen(name)+1 > PTY_MAX_NAME) {
254 fprintf(stderr, "%s: Subsidiary pty filename too long.\n", prog);
255 return 1;
256 };
257 strlcpy(subsid_name, name, PTY_MAX_NAME);
258 /*
259 * If unable to get the subsidiary name, discard the controller file
260 * descriptor, ready to try a search instead.
261 */
262 } else {
263 close(*cntrl);
264 *cntrl = -1;
265 };
266 } else {
267 #endif
268 /*
269 * On systems without /dev/ptmx, or if opening /dev/ptmx failed,
270 * we open one manager terminal after another, until one that isn't
271 * in use by another program is found.
272 *
273 * Open the devices directory.
274 */
275 dir = opendir(PTY_DEV_DIR);
276 if(!dir) {
277 fprintf(stderr, "%s: Couldn't open %s (%s)\n", prog, PTY_DEV_DIR,
278 strerror(errno));
279 return 1;
280 };
281 /*
282 * Look for pseudo-terminal controller device files in the devices
283 * directory.
284 */
285 while(*cntrl < 0 && (file = readdir(dir))) {
286 if(strncmp(file->d_name, PTY_CNTRL, sizeof(PTY_CNTRL)-1) == 0) {
287 /*
288 * Get the common extension of the control and subsidiary filenames.
289 */
290 const char *ext = file->d_name + sizeof(PTY_CNTRL)-1;
291 if(strlen(ext) > PTY_MAX_SUFFIX)
292 continue;
293 /*
294 * Attempt to open the control file.
295 */
296 strlcpy(manager_name, PTY_DEV_DIR, sizeof(manager_name));
297 strlcat(manager_name, PTY_CNTRL, sizeof(manager_name));
298 strlcat(manager_name, ext, sizeof(manager_name));
299 *cntrl = open(manager_name, O_RDWR);
300 if(*cntrl < 0)
301 continue;
302 /*
303 * Attempt to open the matching subsidiary file.
304 */
305 strlcpy(subsid_name, PTY_DEV_DIR, PTY_MAX_NAME);
306 strlcat(subsid_name, PTY_SUBSID, PTY_MAX_NAME);
307 strlcat(subsid_name, ext, PTY_MAX_NAME);
308 };
309 };
310 closedir(dir);
311 #if HAVE_SYSV_PTY
312 };
313 #endif
314 /*
315 * Did we fail to find a pseudo-terminal pair that we could open?
316 */
317 if(*cntrl < 0) {
318 fprintf(stderr, "%s: Unable to find a free pseudo-terminal.\n", prog);
319 return 1;
320 };
321 /*
322 * System V systems require the program that opens the manager to
323 * grant access to the subsidiary side of the pseudo-terminal.
324 */
325 #ifdef HAVE_SYSV_PTY
326 if(grantpt(*cntrl) < 0 ||
327 unlockpt(*cntrl) < 0) {
328 fprintf(stderr, "%s: Unable to unlock terminal (%s).\n", prog,
329 strerror(errno));
330 return 1;
331 };
332 #endif
333 /*
334 * Success.
335 */
336 return 0;
337 }
338
339 /*.......................................................................
340 * Open the subsidiary end of a pseudo-terminal.
341 *
342 * Input:
343 * prog const char * The name of this program.
344 * subsid_name char * The filename of the subsidiary device.
345 * Output:
346 * return int The file descriptor of the successfully opened
347 * subsidiary device, or < 0 on error.
348 */
pty_open_subsid(const char * prog,char * subsid_name)349 static int pty_open_subsid(const char *prog, char *subsid_name)
350 {
351 int fd; /* The file descriptor of the subsidiary device */
352 /*
353 * Place the process in its own process group. In system-V based
354 * OS's, this ensures that when the pseudo-terminal is opened, it
355 * becomes the controlling terminal of the process.
356 */
357 if(setsid() < 0) {
358 fprintf(stderr, "%s: Unable to form new process group (%s).\n", prog,
359 strerror(errno));
360 return -1;
361 };
362 /*
363 * Attempt to open the specified device.
364 */
365 fd = open(subsid_name, O_RDWR);
366 if(fd < 0) {
367 fprintf(stderr, "%s: Unable to open pty subsidiary device (%s).\n",
368 prog, strerror(errno));
369 return -1;
370 };
371 /*
372 * On system-V streams based systems, we need to push the stream modules
373 * that implement pseudo-terminal and termio interfaces. At least on
374 * Solaris, which pushes these automatically when a subsidiary is opened,
375 * this is redundant, so ignore errors when pushing the modules.
376 */
377 #if HAVE_SYSV_PTY
378 (void) ioctl(fd, I_PUSH, "ptem");
379 (void) ioctl(fd, I_PUSH, "ldterm");
380 /*
381 * On BSD based systems other than SunOS 4.x, the following makes the
382 * pseudo-terminal the controlling terminal of the child process.
383 * According to the pseudo-terminal example code in Steven's
384 * Advanced programming in the unix environment, the !defined(CIBAUD)
385 * part of the clause prevents this from being used under SunOS. Since
386 * I only have his code with me, and won't have access to the book,
387 * I don't know why this is necessary.
388 */
389 #elif defined(TIOCSCTTY) && !defined(CIBAUD)
390 if(ioctl(fd, TIOCSCTTY, (char *) 0) < 0) {
391 fprintf(stderr, "%s: Unable to establish controlling terminal (%s).\n",
392 prog, strerror(errno));
393 close(fd);
394 return -1;
395 };
396 #endif
397 return fd;
398 }
399
400 /*.......................................................................
401 * Read input from the controlling terminal of the program, using
402 * gl_get_line(), and feed it to the user's program running in a child
403 * process, via the controller side of the pseudo-terminal. Also pass
404 * data received from the user's program via the conroller end of
405 * the pseudo-terminal, to stdout.
406 *
407 * Input:
408 * prog const char * The name of this program.
409 * cntrl int The file descriptor of the controller end of the
410 * pseudo-terminal.
411 * Output:
412 * return int 0 - OK.
413 * 1 - Error.
414 */
pty_parent(const char * prog,int cntrl)415 static int pty_parent(const char *prog, int cntrl)
416 {
417 GetLine *gl = NULL; /* The gl_get_line() resource object */
418 char *line; /* An input line read from the user */
419 char *rbuff=NULL; /* A buffer for reading from the pseudo terminal */
420 /*
421 * Allocate the gl_get_line() resource object.
422 */
423 gl = new_GetLine(PTY_MAX_LINE, PTY_HIST_SIZE);
424 if(!gl)
425 return pty_stop_parent(1, cntrl, gl, rbuff);
426 /*
427 * Allocate a buffer to use to accumulate bytes read from the
428 * pseudo-terminal.
429 */
430 rbuff = (char *) malloc(PTY_MAX_READ+1);
431 if(!rbuff)
432 return pty_stop_parent(1, cntrl, gl, rbuff);
433 rbuff[0] = '\0';
434 /*
435 * Register an event handler to watch for data appearing from the
436 * user's program on the controller end of the pseudo terminal.
437 */
438 if(gl_watch_fd(gl, cntrl, GLFD_READ, pty_read_from_program, rbuff))
439 return pty_stop_parent(1, cntrl, gl, rbuff);
440 /*
441 * Read input lines from the user and pass them on to the user's program,
442 * by writing to the controller end of the pseudo-terminal.
443 */
444 while((line=gl_get_line(gl, rbuff, NULL, 0))) {
445 if(pty_write_to_fd(cntrl, line, strlen(line)))
446 return pty_stop_parent(1, cntrl, gl, rbuff);
447 rbuff[0] = '\0';
448 };
449 return pty_stop_parent(0, cntrl, gl, rbuff);
450 }
451
452 /*.......................................................................
453 * This is a private return function of pty_parent(), used to release
454 * dynamically allocated resources, close the controller end of the
455 * pseudo-terminal, and wait for the child to exit. It returns the
456 * exit status of the child process, unless the caller reports an
457 * error itself, in which case the caller's error status is returned.
458 *
459 * Input:
460 * waserr int True if the caller is calling this function because
461 * an error occured.
462 * cntrl int The file descriptor of the controller end of the
463 * pseudo-terminal.
464 * gl GetLine * The resource object of gl_get_line().
465 * rbuff char * The buffer used to accumulate bytes read from
466 * the pseudo-terminal.
467 * Output:
468 * return int The desired exit status of the program.
469 */
pty_stop_parent(int waserr,int cntrl,GetLine * gl,char * rbuff)470 static int pty_stop_parent(int waserr, int cntrl, GetLine *gl, char *rbuff)
471 {
472 int status; /* The return status of the child process */
473 /*
474 * Close the controller end of the terminal.
475 */
476 close(cntrl);
477 /*
478 * Delete the resource object.
479 */
480 gl = del_GetLine(gl);
481 /*
482 * Delete the read buffer.
483 */
484 if(rbuff)
485 free(rbuff);
486 /*
487 * Wait for the user's program to end.
488 */
489 (void) wait(&status);
490 /*
491 * Return either our error status, or the return status of the child
492 * program.
493 */
494 return waserr ? 1 : status;
495 }
496
497 /*.......................................................................
498 * Run the user's program, with its stdin and stdout connected to the
499 * subsidiary end of the psuedo-terminal.
500 *
501 * Input:
502 * prog const char * The name of this program.
503 * subsid int The file descriptor of the subsidiary end of the
504 * pseudo terminal.
505 * argv char *[] The argument vector to pass to the user's program,
506 * where argv[0] is the name of the user's program,
507 * and the last argument is followed by a pointer
508 * to NULL.
509 * Output:
510 * return int If this function returns at all, an error must
511 * have occured when trying to overlay the process
512 * with the user's program. In this case 1 is
513 * returned.
514 */
pty_child(const char * prog,int subsid,char * argv[])515 static int pty_child(const char *prog, int subsid, char *argv[])
516 {
517 struct termios attr; /* The terminal attributes */
518 /*
519 * We need to stop the pseudo-terminal from echoing everything that we send it.
520 */
521 if(tcgetattr(subsid, &attr)) {
522 fprintf(stderr, "%s: Can't get pseudo-terminal attributes (%s).\n", prog,
523 strerror(errno));
524 return 1;
525 };
526 attr.c_lflag &= ~(ECHO);
527 while(tcsetattr(subsid, TCSADRAIN, &attr)) {
528 if(errno != EINTR) {
529 fprintf(stderr, "%s: tcsetattr error: %s\n", prog, strerror(errno));
530 return 1;
531 };
532 };
533 /*
534 * Arrange for stdin, stdout and stderr to be connected to the subsidiary
535 * device, ignoring errors that imply that either stdin or stdout is closed.
536 */
537 while(dup2(subsid, STDIN_FILENO) < 0 && errno==EINTR)
538 ;
539 while(dup2(subsid, STDOUT_FILENO) < 0 && errno==EINTR)
540 ;
541 while(dup2(subsid, STDERR_FILENO) < 0 && errno==EINTR)
542 ;
543 /*
544 * Run the user's program.
545 */
546 if(execvp(argv[0], argv) < 0) {
547 fprintf(stderr, "%s: Unable to execute %s (%s).\n", prog, argv[0],
548 strerror(errno));
549 fflush(stderr);
550 _exit(1);
551 };
552 return 0; /* This should never be reached */
553 }
554
555 /*.......................................................................
556 * This is the event-handler that is called by gl_get_line() whenever
557 * there is tet waiting to be read from the user's program, via the
558 * controller end of the pseudo-terminal. See libtecla.h for details
559 * about its arguments.
560 */
GL_FD_EVENT_FN(pty_read_from_program)561 static GL_FD_EVENT_FN(pty_read_from_program)
562 {
563 char *nlptr; /* A pointer to the last newline in the accumulated string */
564 char *crptr; /* A pointer to the last '\r' in the accumulated string */
565 char *nextp; /* A pointer to the next unprocessed character */
566 /*
567 * Get the read buffer in which we are accumulating a line to be
568 * forwarded to stdout.
569 */
570 char *rbuff = (char *) data;
571 /*
572 * New data may arrive while we are processing the current read, and
573 * it is more efficient to display this here than to keep returning to
574 * gl_get_line() and have it display the latest prefix as a prompt,
575 * followed by the current input line, so we loop, delaying a bit at
576 * the end of each iteration to check for more data arriving from
577 * the application, before finally returning to gl_get_line() when
578 * no more input is available.
579 */
580 do {
581 /*
582 * Get the current length of the output string.
583 */
584 int len = strlen(rbuff);
585 /*
586 * Read the text from the program.
587 */
588 int nnew = read(fd, rbuff + len, PTY_MAX_READ - len);
589 if(nnew < 0)
590 return GLFD_ABORT;
591 len += nnew;
592 /*
593 * Nul terminate the accumulated string.
594 */
595 rbuff[len] = '\0';
596 /*
597 * Find the last newline and last carriage return in the buffer, if any.
598 */
599 nlptr = strrchr(rbuff, '\n');
600 crptr = strrchr(rbuff, '\r');
601 /*
602 * We want to output up to just before the last newline or carriage
603 * return. If there are no newlines of carriage returns in the line,
604 * and the buffer is full, then we should output the whole line. In
605 * all cases a new output line will be started after the latest text
606 * has been output. The intention is to leave any incomplete line
607 * in the buffer, for (perhaps temporary) use as the current prompt.
608 */
609 if(nlptr) {
610 nextp = crptr && crptr < nlptr ? crptr : nlptr;
611 } else if(crptr) {
612 nextp = crptr;
613 } else if(len >= PTY_MAX_READ) {
614 nextp = rbuff + len;
615 } else {
616 nextp = NULL;
617 };
618 /*
619 * Do we have any text to output yet?
620 */
621 if(nextp) {
622 /*
623 * If there was already some text in rbuff before this function
624 * was called, then it will have been used as a prompt. Arrange
625 * to rewrite this prefix, plus the new suffix, by moving back to
626 * the start of the line.
627 */
628 if(len > 0)
629 (void) pty_write_to_fd(STDOUT_FILENO, "\r", 1);
630 /*
631 * Write everything up to the last newline to stdout.
632 */
633 (void) pty_write_to_fd(STDOUT_FILENO, rbuff, nextp - rbuff);
634 /*
635 * Start a new line.
636 */
637 (void) pty_write_to_fd(STDOUT_FILENO, "\r\n", 2);
638 /*
639 * Skip trailing carriage returns and newlines.
640 */
641 while(*nextp=='\n' || *nextp=='\r')
642 nextp++;
643 /*
644 * Move any unwritten text following the newline, to the start of the
645 * buffer.
646 */
647 memmove(rbuff, nextp, len - (nextp - rbuff) + 1);
648 };
649 } while(pty_manager_readable(fd, PTY_READ_TIMEOUT));
650 /*
651 * Make the incomplete line in the output buffer the current prompt.
652 */
653 gl_replace_prompt(gl, rbuff);
654 return GLFD_REFRESH;
655 }
656
657 /*.......................................................................
658 * Write a given string to a specified file descriptor.
659 *
660 * Input:
661 * fd int The file descriptor to write to.
662 * string const char * The string to write (of at least 'n' characters).
663 * n int The number of characters to write.
664 * Output:
665 * return int 0 - OK.
666 * 1 - Error.
667 */
pty_write_to_fd(int fd,const char * string,int n)668 static int pty_write_to_fd(int fd, const char *string, int n)
669 {
670 int ndone = 0; /* The number of characters written so far */
671 /*
672 * Do as many writes as are needed to write the whole string.
673 */
674 while(ndone < n) {
675 int nnew = write(fd, string + ndone, n - ndone);
676 if(nnew > 0)
677 ndone += nnew;
678 else if(errno != EINTR)
679 return 1;
680 };
681 return 0;
682 }
683
684 /*.......................................................................
685 * This is the signal handler that is called when the child process
686 * that is running the user's program exits for any reason. It closes
687 * the subsidiary end of the terminal, so that gl_get_line() in the parent
688 * process sees an end of file.
689 */
pty_child_exited(int sig)690 static void pty_child_exited(int sig)
691 {
692 raise(SIGINT);
693 }
694
695 /*.......................................................................
696 * Return non-zero after a given amount of time if there is data waiting
697 * to be read from a given file descriptor.
698 *
699 * Input:
700 * fd int The descriptor to watch.
701 * usec long The number of micro-seconds to wait for input to
702 * arrive before giving up.
703 * Output:
704 * return int 0 - No data is waiting to be read (or select isn't
705 * available).
706 * 1 - Data is waiting to be read.
707 */
pty_manager_readable(int fd,long usec)708 static int pty_manager_readable(int fd, long usec)
709 {
710 #if HAVE_SELECT
711 fd_set rfds; /* The set of file descriptors to check */
712 struct timeval timeout; /* The timeout */
713 FD_ZERO(&rfds);
714 FD_SET(fd, &rfds);
715 timeout.tv_sec = 0;
716 timeout.tv_usec = usec;
717 return select(fd+1, &rfds, NULL, NULL, &timeout) == 1;
718 #else
719 return 0;
720 #endif
721 }
722