1/* 2 * 3Copyright 1990, 1998 The Open Group 4 5Permission to use, copy, modify, distribute, and sell this software and its 6documentation for any purpose is hereby granted without fee, provided that 7the above copyright notice appear in all copies and that both that 8copyright notice and this permission notice appear in supporting 9documentation. 10 11The above copyright notice and this permission notice shall be included in 12all copies or substantial portions of the Software. 13 14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 18AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 21Except as contained in this notice, the name of The Open Group shall not be 22used in advertising or otherwise to promote the sale, use or other dealings 23in this Software without prior written authorization from The Open Group. 24 * 25 * Author: Keith Packard, MIT X Consortium 26 */ 27 28 29#ifdef HAVE_CONFIG_H 30#include "config.h" 31#endif 32 33#include <X11/Xfuncproto.h> 34 35#include <X11/Intrinsic.h> 36#include <X11/StringDefs.h> 37#include <X11/Xatom.h> 38 39#include <X11/Xmu/Atoms.h> 40#include <X11/Xmu/StdSel.h> 41#include <X11/Xmu/SysUtil.h> 42 43#include <X11/Xaw/Form.h> 44#include <X11/Xaw/Label.h> 45#include <X11/Xaw/Command.h> 46#include <X11/Xaw/AsciiText.h> 47#include <X11/Xaw/Dialog.h> 48#include <X11/Xaw/Cardinals.h> 49#include <X11/Xaw/Paned.h> 50#include <X11/Xaw/Box.h> 51 52extern char *_XawTextGetSTRING(TextWidget ctx, XawTextPosition left, 53 XawTextPosition right); 54 55#include <X11/Xos.h> 56#include <X11/Xfuncs.h> 57#include <sys/stat.h> 58#ifndef _POSIX_SOURCE 59#define _POSIX_SOURCE 60#include <stdio.h> 61#undef _POSIX_SOURCE 62#else 63#include <stdio.h> 64#endif 65#include <X11/Shell.h> 66#include <ctype.h> 67#include <stdlib.h> 68#ifdef HAS_OPENPTY 69# ifdef HAVE_UTIL_H 70# include <util.h> 71# endif 72# ifdef HAVE_LIBUTIL_H 73# include <libutil.h> 74# endif 75# ifdef HAVE_PTY_H 76# include <pty.h> 77# endif 78#endif 79 80static void inputReady(XtPointer w, int *source, XtInputId *id); 81static long TextLength(Widget w); 82static void TextReplace(Widget w, int start, int end, XawTextBlock *block); 83static void TextAppend(Widget w, char *s, int len); 84static void TextInsert(Widget w, char *s, int len); 85static Bool ExceededMaxLines(Widget w); 86static void ScrollLine(Widget w); 87 88static Widget top, text; 89 90static XtInputId input_id; 91 92static FILE *input; 93static Boolean regularFile = FALSE; 94 95static Boolean notified; 96static Boolean iconified; 97 98static Atom wm_delete_window; 99static Atom mit_console; 100 101#define MIT_CONSOLE_LEN 12 102#define MIT_CONSOLE "MIT_CONSOLE_" 103static char mit_console_name[255 + MIT_CONSOLE_LEN + 1] = MIT_CONSOLE; 104 105static struct _app_resources { 106 char *file; 107 Boolean stripNonprint; 108 Boolean notify; 109 Boolean daemon; 110 Boolean verbose; 111 Boolean exitOnFail; 112 int saveLines; 113} app_resources; 114 115#define Offset(field) XtOffsetOf(struct _app_resources, field) 116 117static XtResource resources[] = { 118 {"file", "File", XtRString, sizeof (char *), 119 Offset (file), XtRString, "console" }, 120 {"notify", "Notify", XtRBoolean, sizeof (Boolean), 121 Offset (notify), XtRImmediate, (XtPointer)True }, 122 {"stripNonprint", "StripNonprint", XtRBoolean, sizeof (Boolean), 123 Offset (stripNonprint), XtRImmediate, (XtPointer)True }, 124 {"daemon", "Daemon", XtRBoolean, sizeof (Boolean), 125 Offset (daemon), XtRImmediate, (XtPointer)False}, 126 {"verbose", "Verbose", XtRBoolean, sizeof (Boolean), 127 Offset (verbose),XtRImmediate, (XtPointer)False}, 128 {"exitOnFail", "ExitOnFail", XtRBoolean, sizeof (Boolean), 129 Offset (exitOnFail),XtRImmediate, (XtPointer)False}, 130 {"saveLines", "SaveLines", XtRInt, sizeof (int), 131 Offset (saveLines), XtRImmediate, (XtPointer) 0 }, 132}; 133 134#undef Offset 135 136static XrmOptionDescRec options[] = { 137 {"-file", "*file", XrmoptionSepArg, NULL}, 138 {"-notify", "*notify", XrmoptionNoArg, "TRUE"}, 139 {"-nonotify", "*notify", XrmoptionNoArg, "FALSE"}, 140 {"-daemon", "*daemon", XrmoptionNoArg, "TRUE"}, 141 {"-verbose", "*verbose", XrmoptionNoArg, "TRUE"}, 142 {"-exitOnFail", "*exitOnFail", XrmoptionNoArg, "TRUE"}, 143 {"-saveLines", "*saveLines", XrmoptionSepArg, NULL}, 144}; 145 146 147#ifdef linux 148#define USE_FILE 149#define FILE_NAME "/dev/xconsole" 150# if defined (__GLIBC__) && ((__GLIBC__ > 2) || (__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 1)) 151/* 152 * Linux distribution based on glibc 2.1 and higher should use 153 * devpts. This is the fallback if open file FILE_NAME fails. 154 * <werner@suse.de> 155 */ 156# define USE_PTS 157# endif 158#endif 159 160#if !defined (USE_FILE) || defined (linux) 161# include <sys/ioctl.h> 162# if defined (SVR4) || defined (USE_PTS) 163# include <termios.h> 164# ifndef HAS_OPENPTY 165# include <sys/stropts.h> /* for I_PUSH */ 166# endif 167# ifdef sun 168# include <sys/strredir.h> 169# endif 170# endif 171# if defined(TIOCCONS) || defined(SRIOCSREDIR) 172# define USE_PTY 173static int tty_fd, pty_fd; 174# endif 175#endif 176 177#ifdef USE_PTY 178static int get_pty(int *pty, int *tty); 179#endif 180 181/* Copied from xterm/ptyx.h */ 182#ifndef PTYCHAR1 183#define PTYCHAR1 "pqrstuvwxyzPQRSTUVWXYZ" 184#endif /* !PTYCHAR1 */ 185 186#ifndef PTYCHAR2 187#ifdef __FreeBSD__ 188#define PTYCHAR2 "0123456789abcdefghijklmnopqrstuv" 189#else /* !__FreeBSD__ */ 190#define PTYCHAR2 "0123456789abcdef" 191#endif /* !__FreeBSD__ */ 192#endif /* !PTYCHAR2 */ 193 194static void 195OpenConsole(void) 196{ 197 input = 0; 198 if (app_resources.file) 199 { 200 if (!strcmp (app_resources.file, "console")) 201 { 202 /* must be owner and have read/write permission */ 203#if !defined(__NetBSD__) && !defined(__OpenBSD__) 204 struct stat sbuf; 205# if !defined (linux) 206 if (!stat("/dev/console", &sbuf) && 207 (sbuf.st_uid == getuid()) && 208 !access("/dev/console", R_OK|W_OK)) 209# endif 210#endif 211 { 212#ifdef USE_FILE 213# ifdef linux 214 if (!stat(FILE_NAME, &sbuf)) 215# endif 216 input = fopen (FILE_NAME, "r"); 217#endif 218 219#ifdef USE_PTY 220 if (!input && get_pty (&pty_fd, &tty_fd) == 0) 221 { 222# ifdef TIOCCONS 223 int on = 1; 224 if (ioctl (tty_fd, TIOCCONS, (char *) &on) != -1) 225 input = fdopen (pty_fd, "r"); 226# else 227 int consfd = open("/dev/console", O_RDONLY); 228 if (consfd >= 0) 229 { 230 if (ioctl(consfd, SRIOCSREDIR, tty_fd) != -1) 231 input = fdopen (pty_fd, "r"); 232 close(consfd); 233 } 234# endif 235 } 236#endif /* USE_PTY */ 237 } 238 if (input && app_resources.verbose) 239 { 240 char *hostname; 241 TextAppend (text, "Console log for ", 16); 242 hostname = mit_console_name + MIT_CONSOLE_LEN; 243 TextAppend (text, hostname, strlen (hostname)); 244 TextAppend (text, "\n", 1); 245 } 246 } 247 else 248 { 249 regularFile = FALSE; 250 if (access(app_resources.file, R_OK) == 0) 251 { 252 int fd = open (app_resources.file, 253 O_RDONLY | O_NONBLOCK | O_NOCTTY); 254 if (fd != -1) { 255 input = fdopen (fd, "r"); 256 257 if (input) { 258 struct stat sbuf; 259 260 if ((fstat(fd, &sbuf) == 0) && S_ISREG(sbuf.st_mode)) 261 regularFile = TRUE; 262 } 263 else 264 close(fd); 265 } 266 } 267 } 268 if (!input) 269 { 270 if (app_resources.exitOnFail) 271 exit(0); 272 TextAppend (text, "Couldn't open ", 14); 273 TextAppend (text, app_resources.file, strlen (app_resources.file)); 274 TextAppend (text, "\n", 1); 275 } 276 } 277 else 278 input = stdin; 279 280 if (input) 281 { 282 input_id = XtAddInput (fileno (input), (XtPointer) XtInputReadMask, 283 inputReady, (XtPointer) text); 284 } 285} 286 287static void 288CloseConsole (void) 289{ 290 if (input) 291 { 292 XtRemoveInput (input_id); 293 fclose (input); 294 } 295#ifdef USE_PTY 296 close (tty_fd); 297#endif 298} 299 300/*ARGSUSED*/ 301static void _X_NORETURN 302Quit(Widget widget, XEvent *event, String *params, Cardinal *num_params) 303{ 304 exit (0); 305} 306 307static void 308Notify(void) 309{ 310 Arg arglist[1]; 311 char *oldName; 312 char *newName; 313 314 if (!iconified || !app_resources.notify || notified) 315 return; 316 XtSetArg (arglist[0], XtNiconName, &oldName); 317 XtGetValues (top, arglist, 1); 318 newName = malloc (strlen (oldName) + 3); 319 if (!newName) 320 return; 321 sprintf (newName, "%s *", oldName); 322 XtSetArg (arglist[0], XtNiconName, newName); 323 XtSetValues (top, arglist, 1); 324 free (newName); 325 notified = True; 326} 327 328/*ARGSUSED*/ 329static void 330Deiconified(Widget widget, XEvent *event, String *params, Cardinal *num_params) 331{ 332 Arg arglist[1]; 333 char *oldName; 334 char *newName; 335 size_t oldlen; 336 337 iconified = False; 338 if (!app_resources.notify || !notified) 339 return; 340 XtSetArg (arglist[0], XtNiconName, &oldName); 341 XtGetValues (top, arglist, 1); 342 oldlen = strlen (oldName); 343 if (oldlen >= 2) 344 { 345 newName = malloc (oldlen - 1); 346 if (!newName) 347 return; 348 strncpy (newName, oldName, oldlen - 2); 349 newName[oldlen - 2] = '\0'; 350 XtSetArg (arglist[0], XtNiconName, newName); 351 XtSetValues (top, arglist, 1); 352 free (newName); 353 } 354 notified = False; 355} 356 357/*ARGSUSED*/ 358static void 359Iconified(Widget widget, XEvent *event, String *params, Cardinal *num_params) 360{ 361 iconified = True; 362} 363 364/*ARGSUSED*/ 365static void 366Clear(Widget widget, XEvent *event, String *params, Cardinal *num_params) 367{ 368 long last; 369 XawTextBlock block; 370 371 last = TextLength (text); 372 block.ptr = ""; 373 block.firstPos = 0; 374 block.length = 0; 375 block.format = FMT8BIT; 376 TextReplace (text, 0, last, &block); 377} 378 379static XtActionsRec actions[] = { 380 { "Quit", Quit }, 381 { "Iconified", Iconified }, 382 { "Deiconified", Deiconified }, 383 { "Clear", Clear }, 384}; 385 386static void 387stripNonprint(char *b) 388{ 389 char *c; 390 391 c = b; 392 while (*b) 393 { 394 if (isprint (*b) || (isspace (*b) && *b != '\r')) 395 { 396 if (c != b) 397 *c = *b; 398 ++c; 399 } 400 ++b; 401 } 402 *c = '\0'; 403} 404 405static void 406inputReady(XtPointer w, int *source, XtInputId *id) 407{ 408 char buffer[1025]; 409 int n; 410 411 n = read (*source, buffer, sizeof (buffer) - 1); 412 if (n <= 0) 413 { 414 if (app_resources.file && regularFile && n == 0) 415 { 416 if (XPending(XtDisplay(w))) 417 return; 418 419 sleep(1); 420 return; 421 } 422 423 fclose (input); 424 XtRemoveInput (*id); 425 426 /* try to reopen if pipe; this can be caused by syslog restart */ 427 if (app_resources.file && !regularFile && n == 0) 428 { 429 OpenConsole(); 430 } 431 } else { 432 Notify(); 433 buffer[n] = '\0'; 434 if (app_resources.stripNonprint) 435 { 436 stripNonprint (buffer); 437 n = strlen (buffer); 438 } 439 440 TextAppend ((Widget) text, buffer, n); 441 } 442} 443 444static Boolean 445ConvertSelection(Widget w, Atom *selection, Atom *target, Atom *type, 446 XtPointer *value, unsigned long *length, int *format) 447{ 448 Display* d = XtDisplay(w); 449 XSelectionRequestEvent* req = 450 XtGetSelectionRequest(w, *selection, (XtRequestId)NULL); 451 452 if (*target == XA_TARGETS(d)) 453 { 454 Atom* targetP; 455 Atom* std_targets; 456 unsigned long std_length; 457 XmuConvertStandardSelection(w, req->time, selection, target, type, 458 (XPointer *)&std_targets, &std_length, 459 format); 460 *value = (XtPointer)XtMalloc(sizeof(Atom)*(std_length + 5)); 461 targetP = *(Atom**)value; 462 *targetP++ = XA_STRING; 463 *targetP++ = XA_TEXT(d); 464 *targetP++ = XA_LENGTH(d); 465 *targetP++ = XA_LIST_LENGTH(d); 466 *targetP++ = XA_CHARACTER_POSITION(d); 467 *length = std_length + (targetP - (*(Atom **) value)); 468 memmove( (char*)targetP, (char*)std_targets, sizeof(Atom)*std_length); 469 XtFree((char*)std_targets); 470 *type = XA_ATOM; 471 *format = 32; 472 return True; 473 } 474 475 if (*target == XA_LIST_LENGTH(d) || 476 *target == XA_LENGTH(d)) 477 { 478 long * temp; 479 480 temp = (long *) XtMalloc(sizeof(long)); 481 if (*target == XA_LIST_LENGTH(d)) 482 *temp = 1L; 483 else /* *target == XA_LENGTH(d) */ 484 *temp = (long) TextLength (text); 485 486 *value = (XtPointer) temp; 487 *type = XA_INTEGER; 488 *length = 1L; 489 *format = 32; 490 return True; 491 } 492 493 if (*target == XA_CHARACTER_POSITION(d)) 494 { 495 long * temp; 496 497 temp = (long *) XtMalloc(2 * sizeof(long)); 498 temp[0] = (long) 0; 499 temp[1] = TextLength (text); 500 *value = (XtPointer) temp; 501 *type = XA_SPAN(d); 502 *length = 2L; 503 *format = 32; 504 return True; 505 } 506 507 if (*target == XA_STRING || 508 *target == XA_TEXT(d) || 509 *target == XA_COMPOUND_TEXT(d)) 510 { 511 if (*target == XA_COMPOUND_TEXT(d)) 512 *type = *target; 513 else 514 *type = XA_STRING; 515 *length = TextLength (text); 516 *value = (XtPointer)_XawTextGetSTRING((TextWidget) text, 0, *length); 517 *format = 8; 518 /* 519 * Drop our connection to the file; the new console program 520 * will open as soon as it receives the selection contents; there 521 * is a small window where console output will not be redirected, 522 * but I see no way of avoiding that without having two programs 523 * attempt to redirect console output at the same time, which seems 524 * worse 525 */ 526 CloseConsole (); 527 return True; 528 } 529 530 if (XmuConvertStandardSelection(w, req->time, selection, target, type, 531 (XPointer *)value, length, format)) 532 return True; 533 534 return False; 535} 536 537static void _X_NORETURN 538LoseSelection(Widget w, Atom *selection) 539{ 540 Quit (w, (XEvent*)NULL, (String*)NULL, (Cardinal*)NULL); 541} 542 543/*ARGSUSED*/ 544static void 545InsertSelection(Widget w, XtPointer client_data, Atom *selection, Atom *type, 546 XtPointer value, unsigned long *length, int *format) 547{ 548 if (*type != XT_CONVERT_FAIL) 549 TextInsert (text, (char *) value, *length); 550 XtOwnSelection(top, mit_console, CurrentTime, 551 ConvertSelection, LoseSelection, NULL); 552 OpenConsole (); 553} 554 555static void _X_NORETURN 556usage(int exitval) 557{ 558 const char *usage_message = 559 "usage: xconsole [-toolkitoption ...] [-file file-name] [-notify|-nonotify]\n" 560 " [-stripNonprint] [-daemon] [-verbose] [-exitOnFail]\n" 561 " [-saveLines count]\n" 562 " xconsole -help\n" 563 " xconsole -version\n"; 564 565 fputs(usage_message, stderr); 566 exit(exitval); 567} 568 569int 570main(int argc, char *argv[]) 571{ 572 Arg arglist[10]; 573 Cardinal num_args; 574 575 XtSetLanguageProc(NULL,NULL,NULL); 576 577 /* Handle args that don't require opening a display */ 578 for (int n = 1; n < argc; n++) { 579 const char *argn = argv[n]; 580 /* accept single or double dash for -help & -version */ 581 if (argn[0] == '-' && argn[1] == '-') { 582 argn++; 583 } 584 if (strcmp(argn, "-help") == 0) { 585 usage(0); 586 } 587 if (strcmp(argn, "-version") == 0) { 588 puts(PACKAGE_STRING); 589 exit(0); 590 } 591 } 592 593 top = XtInitialize ("xconsole", "XConsole", options, XtNumber (options), 594 &argc, argv); 595 if (argc != 1) { 596 fputs("Unknown argument(s):", stderr); 597 for (int n = 1; n < argc; n++) { 598 fprintf(stderr, " %s", argv[n]); 599 } 600 fputs("\n\n", stderr); 601 usage(1); 602 } 603 604 XtGetApplicationResources (top, (XtPointer)&app_resources, resources, 605 XtNumber (resources), NULL, 0); 606 607 if (app_resources.daemon) 608 if (fork ()) exit (0); 609 XtAddActions (actions, XtNumber (actions)); 610 611 text = XtCreateManagedWidget ("text", asciiTextWidgetClass, 612 top, NULL, 0); 613 614 XtRealizeWidget (top); 615 num_args = 0; 616 XtSetArg(arglist[num_args], XtNiconic, &iconified); num_args++; 617 XtGetValues(top, arglist, num_args); 618 if (iconified) 619 Iconified((Widget)NULL, (XEvent*)NULL, (String*)NULL, (Cardinal*)NULL); 620 else 621 Deiconified((Widget)NULL,(XEvent*)NULL,(String*)NULL,(Cardinal*)NULL); 622 wm_delete_window = XInternAtom(XtDisplay(top), "WM_DELETE_WINDOW", 623 False); 624 (void) XSetWMProtocols (XtDisplay(top), XtWindow(top), 625 &wm_delete_window, 1); 626 627 XmuGetHostname (mit_console_name + MIT_CONSOLE_LEN, 255); 628 629 mit_console = XInternAtom(XtDisplay(top), mit_console_name, False); 630 631 if (XGetSelectionOwner (XtDisplay (top), mit_console)) 632 { 633 XtGetSelectionValue(top, mit_console, XA_STRING, InsertSelection, 634 NULL, CurrentTime); 635 } 636 else 637 { 638 XtOwnSelection(top, mit_console, CurrentTime, 639 ConvertSelection, LoseSelection, NULL); 640 OpenConsole (); 641 } 642 XtMainLoop (); 643 return 0; 644} 645 646static long 647TextLength(Widget w) 648{ 649 return XawTextSourceScan (XawTextGetSource (w), 650 (XawTextPosition) 0, 651 XawstAll, XawsdRight, 1, TRUE); 652} 653 654static void 655TextReplace(Widget w, int start, int end, XawTextBlock *block) 656{ 657 Arg arg; 658 Widget source; 659 XawTextEditType edit_mode; 660 661 source = XawTextGetSource (w); 662 XtSetArg (arg, XtNeditType, &edit_mode); 663 XtGetValues (source, &arg, ONE); 664 XtSetArg (arg, XtNeditType, XawtextEdit); 665 XtSetValues (source, &arg, ONE); 666 XawTextReplace (w, start, end, block); 667 XtSetArg (arg, XtNeditType, edit_mode); 668 XtSetValues (source, &arg, ONE); 669} 670 671static void 672TextAppend(Widget w, char *s, int len) 673{ 674 long last, current; 675 XawTextBlock block; 676 677 current = XawTextGetInsertionPoint (w); 678 last = TextLength (w); 679 block.ptr = s; 680 block.firstPos = 0; 681 block.length = len; 682 block.format = FMT8BIT; 683 /* 684 * If saveLines is 1, just replace the entire contents of the widget 685 * each time, so the test in ExceededMaxLines() isn't fooled. 686 */ 687 if (app_resources.saveLines == 1) 688 TextReplace (w, 0, last, &block); 689 else 690 TextReplace (w, last, last, &block); 691 if (current == last) 692 XawTextSetInsertionPoint (w, last + block.length); 693 if (ExceededMaxLines(w)) 694 ScrollLine(w); 695} 696 697static void 698TextInsert(Widget w, char *s, int len) 699{ 700 XawTextBlock block; 701 long current; 702 703 current = XawTextGetInsertionPoint (w); 704 block.ptr = s; 705 block.firstPos = 0; 706 block.length = len; 707 block.format = FMT8BIT; 708 TextReplace (w, 0, 0, &block); 709 if (current == 0) 710 XawTextSetInsertionPoint (w, len); 711 if (ExceededMaxLines(w)) 712 ScrollLine(w); 713} 714 715static Bool 716ExceededMaxLines(Widget w) 717{ 718 XawTextPosition end_of_last_line; 719 Bool retval = False; 720 721 if (app_resources.saveLines > 0) 722 { 723 /* 724 * XawTextSourceScan() will return the end of the widget if it cannot 725 * find what it is searching for. 726 */ 727 end_of_last_line = XawTextSourceScan (XawTextGetSource (w), 728 (XawTextPosition) 0, 729 XawstEOL, XawsdRight, 730 app_resources.saveLines, TRUE); 731 if (TextLength(w) > end_of_last_line) 732 retval = True; 733 else 734 retval = False; 735 } 736 else 737 retval = False; 738 return retval; 739} 740 741static void 742ScrollLine(Widget w) 743{ 744 XawTextPosition firstnewline; 745 XawTextBlock block; 746 747 /* 748 * This is pretty inefficient but should work well enough unless the 749 * console device is getting totally spammed. Generally, new lines 750 * only come in one at a time anyway. 751 */ 752 firstnewline = XawTextSourceScan (XawTextGetSource (w), 753 (XawTextPosition) 0, 754 XawstEOL, XawsdRight, 1, TRUE); 755 block.ptr = ""; 756 block.firstPos = 0; 757 block.length = 0; 758 block.format = FMT8BIT; 759 TextReplace (w, 0, firstnewline, &block); 760} 761 762#ifdef USE_PTY 763/* 764 * This function opens up a pty master and stuffs its value into pty. 765 * If it finds one, it returns a value of 0. If it does not find one, 766 * it returns a value of !0. This routine is designed to be re-entrant, 767 * so that if a pty master is found and later, we find that the slave 768 * has problems, we can re-enter this function and get another one. 769 */ 770 771static int 772get_pty(int *pty, int *tty) 773{ 774#ifdef HAS_OPENPTY 775 if (openpty(pty, tty, NULL, NULL, NULL) == -1) { 776 return 1; 777 } 778 return 0; 779#else 780 static char ttydev[64], ptydev[64]; 781 782#if defined (SVR4) || defined (USE_PTS) 783 if ((*pty = open ("/dev/ptmx", O_RDWR)) < 0) 784 return 1; 785 grantpt(*pty); 786 unlockpt(*pty); 787 strcpy(ttydev, (char *)ptsname(*pty)); 788 if ((*tty = open(ttydev, O_RDWR)) >= 0) 789 { 790 (void)ioctl(*tty, I_PUSH, "ttcompat"); 791 return 0; 792 } 793 if (*pty >= 0) 794 close (*pty); 795#else /* !SVR4, need lots of code */ 796#ifdef USE_GET_PSEUDOTTY 797 if ((*pty = getpseudotty (&ttydev, &ptydev)) >= 0 && 798 (*tty = open (ttydev, O_RDWR)) >= 0) 799 return 0; 800 if (*pty >= 0) 801 close (*pty); 802#else 803 static int devindex, letter = 0; 804 805 strcpy (ttydev, "/dev/ttyxx"); 806 strcpy (ptydev, "/dev/ptyxx"); 807 while (PTYCHAR1[letter]) { 808 ttydev [strlen(ttydev) - 2] = ptydev [strlen(ptydev) - 2] = 809 PTYCHAR1 [letter]; 810 811 while (PTYCHAR2[devindex]) { 812 ttydev [strlen(ttydev) - 1] = ptydev [strlen(ptydev) - 1] = 813 PTYCHAR2 [devindex]; 814 if ((*pty = open (ptydev, O_RDWR)) >= 0 && 815 (*tty = open (ttydev, O_RDWR)) >= 0) 816 { 817 /* 818 * We need to set things up for our next entry 819 * into this function! 820 */ 821 (void) devindex++; 822 return(0); 823 } 824 if (*pty >= 0) 825 close (*pty); 826 devindex++; 827 } 828 devindex = 0; 829 (void) letter++; 830 } 831#endif /* USE_GET_PSEUDOTTY */ 832#endif /* SVR4 */ 833 /* 834 * We were unable to allocate a pty master! Return an error 835 * condition and let our caller terminate cleanly. 836 */ 837 return(1); 838#endif /* HAS_OPENPTY */ 839} 840#endif 841