diff options
Diffstat (limited to 'xconsole.c')
-rw-r--r-- | xconsole.c | 825 |
1 files changed, 825 insertions, 0 deletions
diff --git a/xconsole.c b/xconsole.c new file mode 100644 index 0000000..ca3db21 --- /dev/null +++ b/xconsole.c @@ -0,0 +1,825 @@ +/* + * $Xorg: xconsole.c,v 1.5 2001/02/09 02:05:40 xorgcvs Exp $ + * +Copyright 1990, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + * + * Author: Keith Packard, MIT X Consortium + */ + +#include <X11/Intrinsic.h> +#include <X11/StringDefs.h> +#include <X11/Xatom.h> + +#include <X11/Xmu/Atoms.h> +#include <X11/Xmu/StdSel.h> + +#include <X11/Xaw/Form.h> +#include <X11/Xaw/Label.h> +#include <X11/Xaw/Command.h> +#include <X11/Xaw/AsciiText.h> +#include <X11/Xaw/Dialog.h> +#include <X11/Xaw/Cardinals.h> +#include <X11/Xaw/Paned.h> +#include <X11/Xaw/Box.h> + +#include <X11/Xos.h> +#include <X11/Xfuncs.h> +#include <sys/stat.h> +#ifndef _POSIX_SOURCE +#define _POSIX_SOURCE +#include <stdio.h> +#undef _POSIX_SOURCE +#else +#include <stdio.h> +#endif +#include <X11/Shell.h> +#include <ctype.h> + +/* Fix ISC brain damage. When using gcc fdopen isn't declared in <stdio.h>. */ +#if defined(ISC) && __STDC__ && !defined(ISC30) +extern FILE *fdopen(int, char const *); +#endif + +static long TextLength (); + +static Widget top, text; + +static XtInputId input_id; + +static FILE *input; + +static Boolean notified; +static Boolean iconified; + +static Atom wm_delete_window; +static Atom mit_console; +#define MIT_CONSOLE_LEN 12 +#define MIT_CONSOLE "MIT_CONSOLE_" +static char mit_console_name[255 + MIT_CONSOLE_LEN + 1] = MIT_CONSOLE; + +static struct _app_resources { + char *file; + Boolean stripNonprint; + Boolean notify; + Boolean daemon; + Boolean verbose; + Boolean exitOnFail; +} app_resources; + +#define Offset(field) XtOffsetOf(struct _app_resources, field) + +static XtResource resources[] = { + {"file", "File", XtRString, sizeof (char *), + Offset (file), XtRString, "console" }, + {"notify", "Notify", XtRBoolean, sizeof (Boolean), + Offset (notify), XtRImmediate, (XtPointer)True }, + {"stripNonprint", "StripNonprint", XtRBoolean, sizeof (Boolean), + Offset (stripNonprint), XtRImmediate, (XtPointer)True }, + {"daemon", "Daemon", XtRBoolean, sizeof (Boolean), + Offset (daemon), XtRImmediate, (XtPointer)False}, + {"verbose", "Verbose", XtRBoolean, sizeof (Boolean), + Offset (verbose),XtRImmediate, (XtPointer)False}, + {"exitOnFail", "ExitOnFail", XtRBoolean, sizeof (Boolean), + Offset (exitOnFail),XtRImmediate, (XtPointer)False}, +}; + +#undef Offset + +static XrmOptionDescRec options[] = { + {"-file", "*file", XrmoptionSepArg, NULL}, + {"-notify", "*notify", XrmoptionNoArg, "TRUE"}, + {"-nonotify", "*notify", XrmoptionNoArg, "FALSE"}, + {"-daemon", "*daemon", XrmoptionNoArg, "TRUE"}, + {"-verbose", "*verbose", XrmoptionNoArg, "TRUE"}, + {"-exitOnFail", "*exitOnFail", XrmoptionNoArg, "TRUE"}, +}; + +#ifdef ultrix +#define USE_FILE +#define FILE_NAME "/dev/xcons" +#endif + +#ifndef USE_FILE +#include <sys/ioctl.h> +#ifdef hpux +#include <termios.h> +#endif +#ifdef SVR4 +#include <termios.h> +#include <sys/stropts.h> /* for I_PUSH */ +#ifdef sun +#include <sys/strredir.h> +#endif +#endif + +#if defined(TIOCCONS) || defined(SRIOCSREDIR) +#define USE_PTY +static int tty_fd, pty_fd; +static char ttydev[64], ptydev[64]; +#endif +#endif + +#if (defined(SVR4) && !defined(sun)) || (defined(SYSV) && defined(i386)) +#define USE_OSM +#include <signal.h> +FILE *osm_pipe(); +static int child_pid; +#endif + +static void inputReady (); + +static +OpenConsole () +{ + input = 0; + if (app_resources.file) + { + if (!strcmp (app_resources.file, "console")) + { + struct stat sbuf; + /* must be owner and have read/write permission */ + if (!stat("/dev/console", &sbuf) && + (sbuf.st_uid == getuid()) && + !access("/dev/console", R_OK|W_OK)) + { +#ifdef USE_FILE + input = fopen (FILE_NAME, "r"); +#endif +#ifdef USE_PTY + if (get_pty (&pty_fd, &tty_fd, ttydev, ptydev) == 0) + { +#ifdef TIOCCONS + int on = 1; + if (ioctl (tty_fd, TIOCCONS, (char *) &on) != -1) + input = fdopen (pty_fd, "r"); +#else + int consfd = open("/dev/console", O_RDONLY); + if (consfd >= 0) + { + if (ioctl(consfd, SRIOCSREDIR, tty_fd) != -1) + input = fdopen (pty_fd, "r"); + close(consfd); + } +#endif + } +#endif + } +#ifdef USE_OSM + /* Don't have to be owner of /dev/console when using /dev/osm. */ + if (!input) + input = osm_pipe(); +#endif + if (input && app_resources.verbose) + { + char *hostname; + TextAppend (text, "Console log for ", 16); + hostname = mit_console_name + MIT_CONSOLE_LEN; + TextAppend (text, hostname, strlen (hostname)); + TextAppend (text, "\n", 1); + } + } + else + { + if (access(app_resources.file, R_OK) == 0) + input = fopen (app_resources.file, "r"); + } + if (!input) + { + if (app_resources.exitOnFail) + exit(0); + TextAppend (text, "Couldn't open ", 14); + TextAppend (text, app_resources.file, strlen (app_resources.file)); + TextAppend (text, "\n", 1); + } + } + else + input = stdin; + + if (input) + { + input_id = XtAddInput (fileno (input), (XtPointer) XtInputReadMask, + inputReady, (XtPointer) text); + } +} + + +static +CloseConsole () +{ + if (input) { + XtRemoveInput (input_id); + fclose (input); + } +#ifdef USE_PTY + close (tty_fd); +#endif +} + +#ifdef USE_OSM +static void +KillChild(sig) + int sig; +{ + if (child_pid > 0) + kill(child_pid, SIGTERM); + exit(0); +} +#endif + +/*ARGSUSED*/ +static void +Quit (widget, event, params, num_params) + Widget widget; + XEvent *event; + String *params; + Cardinal *num_params; +{ +#ifdef USE_OSM + if (child_pid > 0) + kill(child_pid, SIGTERM); +#endif + exit (0); +} + +static int (*ioerror)(); + +#ifdef USE_OSM +static int +IOError(dpy) + Display *dpy; +{ + if (child_pid > 0) + kill(child_pid, SIGTERM); + (*ioerror)(dpy); +} +#endif + +extern char *malloc (); + +static void +Notify () +{ + Arg arglist[1]; + char *oldName; + char *newName; + + if (!iconified || !app_resources.notify || notified) + return; + XtSetArg (arglist[0], XtNiconName, &oldName); + XtGetValues (top, arglist, 1); + newName = malloc (strlen (oldName) + 3); + if (!newName) + return; + sprintf (newName, "%s *", oldName); + XtSetArg (arglist[0], XtNiconName, newName); + XtSetValues (top, arglist, 1); + free (newName); + notified = True; +} + +/*ARGSUSED*/ +static void +Deiconified (widget, event, params, num_params) + Widget widget; + XEvent *event; + String *params; + Cardinal *num_params; +{ + Arg arglist[1]; + char *oldName; + char *newName; + int oldlen; + + iconified = False; + if (!app_resources.notify || !notified) + return; + XtSetArg (arglist[0], XtNiconName, &oldName); + XtGetValues (top, arglist, 1); + oldlen = strlen (oldName); + if (oldlen >= 2) { + newName = malloc (oldlen - 1); + if (!newName) + return; + strncpy (newName, oldName, oldlen - 2); + newName[oldlen - 2] = '\0'; + XtSetArg (arglist[0], XtNiconName, newName); + XtSetValues (top, arglist, 1); + free (newName); + } + notified = False; +} + +/*ARGSUSED*/ +static void +Iconified (widget, event, params, num_params) + Widget widget; + XEvent *event; + String *params; + Cardinal *num_params; +{ + iconified = True; +} + +/*ARGSUSED*/ +static void +Clear (widget, event, params, num_params) + Widget widget; + XEvent *event; + String *params; + Cardinal *num_params; +{ + long last; + XawTextBlock block; + + last = TextLength (text); + block.ptr = ""; + block.firstPos = 0; + block.length = 0; + block.format = FMT8BIT; + TextReplace (text, 0, last, &block); +} + +static XtActionsRec actions[] = { + "Quit", Quit, + "Iconified", Iconified, + "Deiconified", Deiconified, + "Clear", Clear, +}; + +static void +stripNonprint (b) + char *b; +{ + char *c; + + c = b; + while (*b) + { + if (isprint (*b) || isspace (*b) && *b != '\r') + { + if (c != b) + *c = *b; + ++c; + } + ++b; + } + *c = '\0'; +} + +static void +inputReady (w, source, id) + XtPointer w; + int *source; + XtInputId *id; +{ + char buffer[1025]; + int n; + + n = read (*source, buffer, sizeof (buffer) - 1); + if (n <= 0) + { + fclose (input); + XtRemoveInput (*id); + } + Notify (); + buffer[n] = '\0'; + if (app_resources.stripNonprint) + { + stripNonprint (buffer); + n = strlen (buffer); + } + TextAppend ((Widget) text, buffer, n); +} + +static Boolean +ConvertSelection (w, selection, target, type, value, length, format) + Widget w; + Atom *selection, *target, *type; + XtPointer *value; + unsigned long *length; + int *format; +{ + Display* d = XtDisplay(w); + XSelectionRequestEvent* req = + XtGetSelectionRequest(w, *selection, (XtRequestId)NULL); + + if (*target == XA_TARGETS(d)) { + Atom* targetP; + Atom* std_targets; + unsigned long std_length; + XmuConvertStandardSelection(w, req->time, selection, target, type, + (caddr_t*)&std_targets, &std_length, format); + *value = (XtPointer)XtMalloc(sizeof(Atom)*(std_length + 5)); + targetP = *(Atom**)value; + *targetP++ = XA_STRING; + *targetP++ = XA_TEXT(d); + *targetP++ = XA_LENGTH(d); + *targetP++ = XA_LIST_LENGTH(d); + *targetP++ = XA_CHARACTER_POSITION(d); + *length = std_length + (targetP - (*(Atom **) value)); + memmove( (char*)targetP, (char*)std_targets, sizeof(Atom)*std_length); + XtFree((char*)std_targets); + *type = XA_ATOM; + *format = 32; + return True; + } + + if (*target == XA_LIST_LENGTH(d) || + *target == XA_LENGTH(d)) + { + long * temp; + + temp = (long *) XtMalloc(sizeof(long)); + if (*target == XA_LIST_LENGTH(d)) + *temp = 1L; + else /* *target == XA_LENGTH(d) */ + *temp = (long) TextLength (text); + + *value = (XtPointer) temp; + *type = XA_INTEGER; + *length = 1L; + *format = 32; + return True; + } + + if (*target == XA_CHARACTER_POSITION(d)) + { + long * temp; + + temp = (long *) XtMalloc(2 * sizeof(long)); + temp[0] = (long) 0; + temp[1] = TextLength (text); + *value = (XtPointer) temp; + *type = XA_SPAN(d); + *length = 2L; + *format = 32; + return True; + } + + if (*target == XA_STRING || + *target == XA_TEXT(d) || + *target == XA_COMPOUND_TEXT(d)) + { + extern char *_XawTextGetSTRING(); + if (*target == XA_COMPOUND_TEXT(d)) + *type = *target; + else + *type = XA_STRING; + *length = TextLength (text); + *value = (XtPointer)_XawTextGetSTRING((TextWidget) text, 0, *length); + *format = 8; + /* + * Drop our connection to the file; the new console program + * will open as soon as it receives the selection contents; there + * is a small window where console output will not be redirected, + * but I see no way of avoiding that without having two programs + * attempt to redirect console output at the same time, which seems + * worse + */ + CloseConsole (); + return True; + } + + if (XmuConvertStandardSelection(w, req->time, selection, target, type, + (caddr_t *)value, length, format)) + return True; + + return False; +} + +static void +LoseSelection (w, selection) + Widget w; + Atom *selection; +{ + Quit (w, (XEvent*)NULL, (String*)NULL, (Cardinal*)NULL); +} + +/*ARGSUSED*/ +static void +InsertSelection (w, client_data, selection, type, value, length, format) + Widget w; + XtPointer client_data; + Atom *selection, *type; + XtPointer value; + unsigned long *length; + int *format; +{ + if (*type != XT_CONVERT_FAIL) + TextInsert (text, (char *) value, *length); + XtOwnSelection(top, mit_console, CurrentTime, + ConvertSelection, LoseSelection, NULL); + OpenConsole (); +} + + +main (argc, argv) + char **argv; +{ + Arg arglist[10]; + Cardinal num_args; + + top = XtInitialize ("xconsole", "XConsole", options, XtNumber (options), + &argc, argv); + XtGetApplicationResources (top, (XtPointer)&app_resources, resources, + XtNumber (resources), NULL, 0); + + if (app_resources.daemon) + if (fork ()) exit (0); + XtAddActions (actions, XtNumber (actions)); + + text = XtCreateManagedWidget ("text", asciiTextWidgetClass, + top, NULL, 0); + + XtRealizeWidget (top); + num_args = 0; + XtSetArg(arglist[num_args], XtNiconic, &iconified); num_args++; + XtGetValues(top, arglist, num_args); + if (iconified) + Iconified((Widget)NULL, (XEvent*)NULL, (String*)NULL, (Cardinal*)NULL); + else + Deiconified((Widget)NULL,(XEvent*)NULL,(String*)NULL,(Cardinal*)NULL); + wm_delete_window = XInternAtom(XtDisplay(top), "WM_DELETE_WINDOW", + False); + (void) XSetWMProtocols (XtDisplay(top), XtWindow(top), + &wm_delete_window, 1); + + XmuGetHostname (mit_console_name + MIT_CONSOLE_LEN, 255); + + mit_console = XInternAtom(XtDisplay(top), mit_console_name, False); + + if (XGetSelectionOwner (XtDisplay (top), mit_console)) + { + XtGetSelectionValue(top, mit_console, XA_STRING, InsertSelection, + NULL, CurrentTime); + } + else + { + XtOwnSelection(top, mit_console, CurrentTime, + ConvertSelection, LoseSelection, NULL); + OpenConsole (); + } +#ifdef USE_OSM + ioerror = XSetIOErrorHandler(IOError); +#endif + XtMainLoop (); + return 0; +} + +static long TextLength (w) + Widget w; +{ + return XawTextSourceScan (XawTextGetSource (w), + (XawTextPosition) 0, + XawstAll, XawsdRight, 1, TRUE); +} + +TextReplace (w, start, end, block) + Widget w; + XawTextBlock *block; +{ + Arg arg; + Widget source; + XawTextEditType edit_mode; + + source = XawTextGetSource (w); + XtSetArg (arg, XtNeditType, &edit_mode); + XtGetValues (source, &arg, ONE); + XtSetArg (arg, XtNeditType, XawtextEdit); + XtSetValues (source, &arg, ONE); + XawTextReplace (w, start, end, block); + XtSetArg (arg, XtNeditType, edit_mode); + XtSetValues (source, &arg, ONE); +} + +TextAppend (w, s, len) + Widget w; + char *s; +{ + long last, current; + XawTextBlock block; + + current = XawTextGetInsertionPoint (w); + last = TextLength (w); + block.ptr = s; + block.firstPos = 0; + block.length = len; + block.format = FMT8BIT; + TextReplace (w, last, last, &block); + if (current == last) + XawTextSetInsertionPoint (w, last + block.length); +} + +TextInsert (w, s, len) + Widget w; + char *s; +{ + XawTextBlock block; + long current; + + current = XawTextGetInsertionPoint (w); + block.ptr = s; + block.firstPos = 0; + block.length = len; + block.format = FMT8BIT; + TextReplace (w, 0, 0, &block); + if (current == 0) + XawTextSetInsertionPoint (w, len); +} + +#ifdef USE_PTY +/* This function opens up a pty master and stuffs it's value into pty. + * If it finds one, it returns a value of 0. If it does not find one, + * it returns a value of !0. This routine is designed to be re-entrant, + * so that if a pty master is found and later, we find that the slave + * has problems, we can re-enter this function and get another one. + */ + +#include "../xterm/ptyx.h" + +get_pty (pty, tty, ttydev, ptydev) + int *pty, *tty; + char *ttydev, *ptydev; +{ +#ifdef SVR4 + if ((*pty = open ("/dev/ptmx", O_RDWR)) < 0) { + return 1; + } + grantpt(*pty); + unlockpt(*pty); + strcpy(ttydev, (char *)ptsname(*pty)); + if ((*tty = open(ttydev, O_RDWR)) >= 0) { + (void)ioctl(*tty, I_PUSH, "ttcompat"); + return 0; + } + if (*pty >= 0) + close (*pty); +#else /* !SVR4, need lots of code */ +#ifdef USE_GET_PSEUDOTTY + if ((*pty = getpseudotty (&ttydev, &ptydev)) >= 0 && + (*tty = open (ttydev, O_RDWR)) >= 0) + return 0; + if (*pty >= 0) + close (*pty); +#else + static int devindex, letter = 0; + +#if defined(umips) && defined (SYSTYPE_SYSV) + struct stat fstat_buf; + + *pty = open ("/dev/ptc", O_RDWR); + if (*pty < 0 || (fstat (*pty, &fstat_buf)) < 0) { + return(1); + } + sprintf (ttydev, "/dev/ttyq%d", minor(fstat_buf.st_rdev)); + sprintf (ptydev, "/dev/ptyq%d", minor(fstat_buf.st_rdev)); + if ((*tty = open (ttydev, O_RDWR)) >= 0) { + /* got one! */ + return(0); + } + close (*pty); +#else /* not (umips && SYSTYPE_SYSV) */ +#ifdef CRAY + for (; devindex < 256; devindex++) { + sprintf (ttydev, "/dev/ttyp%03d", devindex); + sprintf (ptydev, "/dev/pty/%03d", devindex); + + if ((*pty = open (ptydev, O_RDWR)) >= 0 && + (*tty = open (ttydev, O_RDWR)) >= 0) + { + /* We need to set things up for our next entry + * into this function! + */ + (void) devindex++; + return(0); + } + if (*pty >= 0) + close (*pty); + } +#else /* !CRAY */ +#ifdef sgi + { + char *slave; + slave = _getpty (pty, O_RDWR, 0622, 0); + if ((*tty = open (slave, O_RDWR)) != -1) + return 0; + } +#else + strcpy (ttydev, "/dev/ttyxx"); + strcpy (ptydev, "/dev/ptyxx"); + while (PTYCHAR1[letter]) { + ttydev [strlen(ttydev) - 2] = ptydev [strlen(ptydev) - 2] = + PTYCHAR1 [letter]; + + while (PTYCHAR2[devindex]) { + ttydev [strlen(ttydev) - 1] = ptydev [strlen(ptydev) - 1] = + PTYCHAR2 [devindex]; + if ((*pty = open (ptydev, O_RDWR)) >= 0 && + (*tty = open (ttydev, O_RDWR)) >= 0) + { + /* We need to set things up for our next entry + * into this function! + */ + (void) devindex++; + return(0); + } + if (*pty >= 0) + close (*pty); + devindex++; + } + devindex = 0; + (void) letter++; + } +#endif /* sgi else not sgi */ +#endif /* CRAY else not CRAY */ +#endif /* umips && SYSTYPE_SYSV */ +#endif /* USE_GET_PSEUDOTTY */ +#endif /* SVR4 */ + /* We were unable to allocate a pty master! Return an error + * condition and let our caller terminate cleanly. + */ + return(1); +} +#endif + +#ifdef USE_OSM +/* + * On SYSV386 there is a special device, /dev/osm, where system messages + * are sent. Problem is that we can't perform a select(2) on this device. + * So this routine creates a streams-pty where one end reads the device and + * sends the output to xconsole. + */ + +#ifdef USL +#define OSM_DEVICE "/dev/osm2" +#define NO_READAHEAD +#else +#define OSM_DEVICE "/dev/osm" +#endif + +FILE * +osm_pipe() +{ + int tty; + char ttydev[64]; + + if (access("/dev/osm", R_OK) < 0) return NULL; + if ((tty = open("/dev/ptmx", O_RDWR)) < 0) return NULL; + + grantpt(tty); + unlockpt(tty); + strcpy(ttydev, (char *)ptsname(tty)); + + if ((child_pid = fork()) == 0) { + int pty, osm, nbytes, skip; + char cbuf[128]; + + skip = 0; +#ifndef NO_READAHEAD + osm = open(OSM_DEVICE, O_RDONLY); + if (osm >= 0) { + while ((nbytes = read(osm, cbuf, sizeof(cbuf))) > 0) + skip += nbytes; + close(osm); + } +#endif + pty = open(ttydev, O_RDWR); + if (pty < 0) exit(1); + osm = open(OSM_DEVICE, O_RDONLY); + if (osm < 0) exit(1); + for (nbytes = 0; skip > 0 && nbytes >= 0; skip -= nbytes) { + nbytes = skip; + if (nbytes > sizeof(cbuf)) + nbytes = sizeof(cbuf); + nbytes = read(osm, cbuf, nbytes); + } + while ((nbytes = read(osm, cbuf, sizeof(cbuf))) >= 0) + write(pty, cbuf, nbytes); + exit(0); + } + signal(SIGHUP, KillChild); + signal(SIGINT, KillChild); + signal(SIGTERM, KillChild); + return fdopen(tty, "r"); +} +#endif /* USE_OSM */ |