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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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