Mac OS X Server 1.x: About Terminal

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:
Article 30925: "Mac OS X Server: Release Notes"
Note: This article pertains to Mac OS X Server versions 1.x, which were released prior to May 2001.

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.

Known Issues

These new bugs have appeared since OpenStep for Mach 4.2.

Issue The Page Up and Page Down keys don't work.
Issue 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

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);
}

Published Date: Feb 18, 2012