TITLE
    Mac OS X Server: About Terminal
Article ID:
Created:
Modified:
60128
3/11/99
3/26/99

TOPIC

    This article contains release notes for the the Mac OS X Server release of the Terminal application.

    Note: This document was installed by Mac OS X Server in /System/Documentation/ReadMe. For a list of other release notes see: Tech Info Library Article 30925: " Mac OS X Server: Release Notes "


DISCUSSION

    For those familiar with Terminal in OpenStep for Mach, notes specific to this release are included. Also, a section is devoted to some of Terminal's less obvious features. Finally, this document includes information about Terminal's execution service, which is of interest mainly to developers.

    New Features

    The following new features have been added to the Terminal application in this release.

    * Terminal has been moved to the directory /System/Administration (it used to live in /NextApps ).



    * Yellow Box Conversion and Macintosh User Interface: Terminal has been ported to use the Yellow Box APIs and to support the Macintosh menu layout and user interface. In addition, Terminal's execution service uses NSConnection-based ("new") Distributed Objects.


    * Library Menu: You can configure Terminal to create a menu containing shortcuts for the terminal settings files in your ~/Library/Terminal directory. Enable this feature by executing the following command:
          /usr/bin/defaults write Terminal LibraryMenu YES
        After restarting Terminal, the Shell>Library menu contains an item for each of the abovementioned files.
    Known Problems

    These new bugs have appeared since OpenStep for Mach 4.2.

    Problem The Page Up and Page Down keys don't work.
    Problem Characters lost when pasting text.
    Description Pasting a large body of text into a Terminal window will sometimes result in many of the characters being lost.

    Terminal Features

    * Saved Windows : You can save a window or set of windows to a file, thereby saving your preferred configurations. Everything about the windows is saved except the contents of the scrollback buffer; this includes the shell, the location of the window, and whether the window is miniaturized. When you choose the Save As command, you can decide whether all windows or just the main window is saved.


      Once a window is associated with a file, Save can be used to flush the settings out without seeing a Save Panel, just as with more conventional documents. However, if more than one window belongs with that file, all the relevant windows will be re-saved (the Save menu item indicates this by changing its label to Save Set). This convention allows you to open a set of windows, rearrange them, and type Command-s to save them back to the file.

      To have a file opened for you automatically each time you start up Terminal, you can either check a box in the Save As panel, or you can specify the filename in the Preferences panel under Startup options.


    * Drag and Drop Support : Terminal can accept files dragged into a window: The appropriate filenames will be pasted, plus a space. Dragging an executable file onto Terminal's application icon results in that file being executed in a new Terminal window. If a directory is dragged, a new shell window is started with that directory as the working directory.


    * Color Setting : You can drag and drop colors into Terminal windows to allow reverse video and other interesting settings. The color of the selection is settable, as are the color and size of the cursor.


    * Activity Monitoring : Terminal tries to determine whether your shell windows are in active use by monitoring the processes inside them. If Terminal thinks something interesting is going on inside a window, it will mark the window with a broken X. You are prompted for confirmation before if you try to close a "busy" window or quit Terminal when there are busy windows.

      To make this evaluation, Terminal collects ps -type information about processes it considers relevant and applies a number of heuristics. Usually, Terminal considers shells and a few other garden-variety processes such as su to be innocuous and will not mark windows busy on their account. But there are exceptions; for example, if the distinguished process on the tty is a shell but it is actively running, the window will be dirty. You may designate additional "clean" command names ( rlogin and telnet are common) in the preferences panel. If there are running processes on the terminal, the terminal might still be clean if the processes seem to be running in the background. But an interesting process that's the current process or that you've explicitly suspended with Control-z will always make the window dirty.

      The status of windows is checked only when it is likely to have changed. A back-off strategy prevents the probes from happening too often if the window seems constantly busy.


    * Output Indication : When Terminal is hidden and one or more Terminal windows have received unviewed output, Terminal puts a blinking icon in the menu bar. Also, Terminal window shades are marked as `dirty' when there is unviewed output.


    * Accelerators : Two panels, Quick Title and New Command, provide an easy way to set the title on a window and to start up a new window running a designated command. Both of these panels would otherwise require use of the Preferences panel.

      Quick Title provides the current title of the window for you to edit; however, when you set the title this way Terminal won't update any of the information in the future. For example, if your window is titled "/bin/csh 80x24 ~/Term.term" and you edit that to include some text of your own choosing, the new title is treated as a string and won't reflect (for example) the size of the window should you change it. The preferences panel allows better control over this; you can mix your own material with Terminal's automatically updated information.

      New Command does use path searching when it execs your command, but since the path is just the default inherited from Workspace, it's utility is limited. You can add arguments, but shell metacharacters won't be handled correctly.


    * A Tool for Creating New Terminal Windows : The program /usr/bin/terminal may be useful for opening new shell windows in the current (or another) directory. Without any arguments, it will open a new shell window in the current directory. (However, you may have a .login or .cshrc file which defeats this by changing to the user's home directory; you may wish to change this.) If a command is specified, terminal will, by default, run the command without a shell for fastest startup. If a shell is desired, it can be guaranteed by running the command with the -shell option. For usage information, type `/usr/bin/terminal -help'. For information on how this utility works, see the section Terminal's Execution Service below.


    * Execution Service : Terminal publishes a Distributed Objects interface to allow it to execute commands on the behalf of other applications. See the section Terminal's Execution Service below.


    * Settable Title Bar Text : A process can set the title bar of its terminal window by emitting the following string, without spaces or quotes:

        ESC ] 0 ; "Title String" BEL

        where ESC is '\033', and BEL is '\007'. The initial 0 may alternatively be a 2. The following csh script code (which can be added to a .cshrc file) shows one example of how you can set the title bar to a "clipped" version of the current directory. It also puts the hostname in the prompt in inverse video:



        # begin csh script to set the title bar to the current working directory
        set homedirectory = ~${user}
        alias shortpwd 'pwd|sed -e "s|^/private||" -e "s|${homedirectory}|~${user}|"'
        alias getTitle 'set titleString="`shortpwd|sed -e '\''s|.*[^/]\(/[^/]*/[^/]*/[^/]*/[^/]*\)|...\1|'\'' `"'
        getTitle

        if( ${?prompt} ) then
        set hostNm=`hostname`
        alias cd 'cd \!*; getTitle ; echo -n "*]0;${titleString}*" ; set prompt = "*[7m${hostNm} >*[m "'
        endif
        cd .
        #end csh script

    * Terminal Services: You can configure Rhapsody Services to allow you to specify Unix commands to operate on text. (Like other OpenStep Services, these will be available when you're editing text in any application). Terminal services are dynamic and appear in other application's Services menu as soon as you define them. They can be edited by choosing 'Terminal Services...' under Terminal's Apple menu.

      When you specify the command associated with a service, you can use the tokens "%s" and "%p" to refer to the location where the selection and prompted input are inserted, respectively (prompted input is not requested unless this string appears in the command).


    * Environment Variables : It is sometimes useful to know the tool that is providing the terminal emulation without changing the TERM environment variable. Terminal now sets 2 environment variables to provide this information to subprocesses:

        TERM_PROGRAM=Apple_Terminal
        TERM_PROGRAM_VERSION=99.99  (or something else...)

    Terminal's Execution Service

    Overview

    The TSTerminalDOServices protocol, declared in the header /System/Developer/Headers/Apps/TerminalDOProtocol.h , allows applications to request that the Terminal application perform commands on their behalf. This is useful if a program wants to open up a shell window for the user to interact with, and it may be easier than setting up pipes and doing a fork / exec of a program yourself.

    Terminal cannot provide services if it is not running. It may be necessary to use the Application Kit's NSWorkspace API to launch Terminal if the connection to Terminal fails.

    You may run the commands in a window, in which case an integer window handle is returned to allow a window to be reused for subsequent commands, or the commands can performed in the background. In addition, you can specify the window's title, as well as the working directory and environment to be used when executing the command.

    In the case of commands performed in the background only, you can pass in an NSData object whose data will be fed to the command's standard input, and if you pass in a pointer to an NSData, Terminal can hand you back the output of the command's standard output and standard error. If you pass a pointer to get these back, the data at the pointer should be nil before you invoke the command. Upon completion, the pointer points to an newly created NSData containing the output.

    For details on the specific methods in the protocol, see the header file referenced above.

    Security Considerations

    By default, Terminal registers a port for the execution service with the Mach bootstrap server. This means that only processes descended from the Workspace application have access to this service. You can also have Terminal register this port with the Mach netname server, which allows access from other machines on the network. This is enabled by setting the default


    /usr/bin/defaults write Terminal PublicDOServices YES

    Important Note

    Running with this default is a security hazard , as Terminal has no way of verifying who is making the remote connection.

    Disabling the Service

    Any user may disable this service by executing the following command:


    /usr/bin/defaults write Terminal DisableDOServices YES

    The superuser can disable it for all users on a given machine by giving this command:


    touch /System/Administration/Terminal.app/.DisableDOServices

    Sample Code and Client

    The program /usr/bin/terminal , mentioned above in "A Tool for Creating New Terminal Windows," is a simple client which utilizes this protocol. The following is the source code for this program:


    //
    //  terminal.m
    //
    //        Sample client for Terminal.app's Distributed Objects services.
    //        Copyright 1994-1997, Apple Computer, Inc. All rights reserved.
    //
    //
    //    This program opens a terminal window in the current directory. It's installed
    //    in /usr/bin/terminal.
    //
    //    Author: sam streeper
    //    Updated by: Barry Locklear, Grant Baillie
    //
    //    To compile:
    //
    //      cc terminal.m -I/System/Developer/Headers -framework Foundation -o terminal
    //
    //    Usage:
    // terminal [-shell] [-dir <dir>] [-noclose] [-env] [command]
    //

    #import <AppKit/AppKit.h>
    #import <Foundation/Foundation.h>
    #import <stdio.h>
    #import <mach/mach.h>
    #import <servers/bootstrap.h>
    #import <Apps/TerminalDOProtocol.h>

    static void usage(void) {
    fprintf(stderr,"terminal: open a new Terminal.app window\n\n");
    fprintf(stderr,"Usage: terminal [-shell] [-dir <dir>] [-noclose] [-env] [command]\n");
    fprintf(stderr," -shell   - Run command from default shell\n");
    fprintf(stderr," -dir     - Specify working directory (default is current)\n");
    fprintf(stderr," -noclose - Retain window upon exit\n");
    fprintf(stderr," -env     - Export current environment\n");
    exit(-1);
    }

    static id<TSTerminalDOServices> lookupTerminalDOServer(void) {
    port_t sendMachPort;
    NSDistantObject *rootProxy = nil;
    id<TSTerminalDOServices> result;

    // First, try look up Terminal's DO object in the bootstrap server.
    // This is where the app registers it by default.
    if ((BOOTSTRAP_SUCCESS == bootstrap_look_up(bootstrap_port, "TerminalDO", &sendMachPort)) && (PORT_NULL != sendMachPort)) {
    NSConnection *conn = [NSConnection connectionWithReceivePort:[NSPort port] sendPort:[NSPort portWithMachPort:sendMachPort]];
    rootProxy = [conn rootProxy];
    }

    // If the previous call failed, the following might succeed if the user
    // logged in is running Terminal with the PublicDOServices user default
    // set.
    if (!rootProxy) {
    rootProxy = [NSConnection rootProxyForConnectionWithRegisteredName:@"TerminalDO" host:@""];
    }

    // We could also try to launch Terminal at this point, using
    // the NSWorkspace protocol.
    if (!rootProxy) {
    fprintf(stderr,"Can't connect to Terminal\n");
    exit(-1);
    }

    [rootProxy setProtocolForProxy:@protocol(TSTerminalDOServices)];
    result = (id<TSTerminalDOServices>)rootProxy;

    if ([result protocolVersion] < 0x10002) {
    fprintf(stderr,"Incompatible terminal protocol version\n");
    exit(-4);
    }

    return result;
    }

    int main(void) {
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    id <TSTerminalDOServices> terminal = lookupTerminalDOServer();

    NSEnumerator *argsEnum = [[[NSProcessInfo processInfo] arguments] objectEnumerator];
    NSString *thisArg;

    int returnCode;
    NSMutableString *command = nil;
    NSDictionary *environment = nil;
    NSString *directory = nil;
    TSShellType shellType = TSShellNone;
    TSExitAction exitAction = TSCloseUnlessError;

    int winHandle;

    NSData *inputData = nil;
    NSData *outputData = nil;
    NSData *errorData = nil;


    thisArg = [argsEnum nextObject]; /* Skip the program name */

    if (nil == thisArg) {
    shellType = TSShellDefault;
    }

    // Run through the command-line arguments. We only set command to
    // something non-nil once we encounter an argument that isn't an
    // option. Once we've done this, though, we treat the rest of
    // the arguments as part of the command.
    //
    while(thisArg = [argsEnum nextObject]) {
    if (command) {
    [command appendFormat:@" %@", thisArg];
    }
    else if (![thisArg hasPrefix:@"-"]) {
    command = [[thisArg mutableCopy] autorelease];
    }
    else  if ([@"-shell" isEqualToString:thisArg]) {
    shellType = TSShellDefault;
    }
    else if ([@"-dir" isEqualToString:thisArg]) {
    if (!(thisArg = [argsEnum nextObject])) usage();
    directory = thisArg;
    }
    else if ([@"-noclose" isEqualToString:thisArg]) {
    exitAction = TSDontCloseOnExit;
    }
    else if ([@"-env" isEqualToString:thisArg]) {
    environment = [[NSProcessInfo processInfo] environment];
    }
    else {
    usage();
    }
    }

    if (!directory) directory = [[NSFileManager defaultManager] currentDirectoryPath];
    if (!command) command = [NSMutableString stringWithCString:""];

    [terminal runCommand:command
    inputData:inputData
    outputData:&outputData
    errorData:&errorData
    waitForReturn:NO
    windowType:TSWindowNew
    windowHandle:&winHandle
    exitAction:exitAction
    shellType:shellType
    windowTitle:command
    directory:directory
    environment:environment
    returnCode:&returnCode];

    [pool release];

    exit(returnCode);
    }


Document Information
Product Area: Mac OS System Software
Category: Mac OS X Server
Sub Category: General Topics

Copyright © 2000 Apple Computer, Inc. All rights reserved.