4 pty support by Joe Freeman
5 Subprocess Example, Release 2.0
8 You may freely copy, distribute and reuse the code in this example.
9 NeXT disclaims any warranty of any kind, expressed or implied, as to
10 its fitness for any particular use.
13 #import "Subprocess.h"
14 // #import <sgtty.h> // needed to compile under Release 1.0
15 #import <appkit/nextstd.h>
16 #import <appkit/Application.h>
17 #import <appkit/Panel.h>
20 #define PTY_TEMPLATE "/dev/pty??"
23 static void showError();
26 /*==========================================================
28 * Private Instance Methods
30 *==========================================================*/
32 @interface Subprocess(Private)
34 - fdHandler:(int)theFd;
37 @implementation Subprocess(Private)
40 // cleanup after a child process exits
44 union wait exitstatus;
47 DPSRemoveFD(fromChild);
50 // Cleanup zombie processes. (blocking wait is too dangerous here...)
51 waitresult = wait4(childPid, &exitstatus, WNOHANG, NULL);
52 if (waitresult != childPid) {
53 /* XXX should handle this gracefully, e.g, timed entry. */
55 childPid=0; // specify that child is dead
58 if ([delegate respondsTo:@selector(subprocessDone:)])
59 [delegate perform:@selector(subprocessDone:) with:self];
60 else if ([delegate respondsTo:@selector(subprocessDone)])
61 [delegate perform:@selector(subprocessDone)];
67 - fdHandler:(int)theFd
68 // DPS handler for output from subprocess
70 if ((bufferCount = read(theFd, outputBuffer, BUFFERSIZE-1)) <= 0)
75 outputBuffer[bufferCount] = '\0';
78 if ([delegate respondsTo:@selector(subprocess:output:)])
79 [delegate perform:@selector(subprocess:output:)
80 with:self with:(void *)&outputBuffer];
81 else if ([delegate respondsTo:@selector(subprocessOutput:)])
82 [delegate perform:@selector(subprocessOutput:)
83 with:(void *)&outputBuffer];
91 /*==========================================================
93 * Private Utility Routines
95 *==========================================================*/
98 showError (const char *errorString, id theDelegate)
99 // ensure errors never get dropped on the floor
101 if (theDelegate && [theDelegate respondsTo:@selector(subprocessError:)])
103 perform:@selector(subprocessError:)
104 with:(void *)errorString];
105 else if (NXApp) // no delegate, but we're running w/in an App
106 NXRunAlertPanel(0, errorString, 0, 0, 0);
112 fdHandler (int theFd, id self)
113 // DPS handler for output from subprocess
115 [self fdHandler:theFd];
119 getptys (int *master, int *slave)
120 // attempt to setup the ptys
122 char device[PTY_LENGTH];
124 char *blockLoc; // specifies the location of block for the device string
125 char *numLoc; // specifies the pty name with the digit ptyxD
126 char *msLoc; // specifies the master (ptyxx) or slave (ttyxx)
129 {B9600, B9600, (char)0x7f, (char)0x15, (CRMOD|ANYP)};
131 {CINTR, CQUIT, CSTART, CSTOP, CEOF, CBRK};
132 struct ltchars sltc =
133 {CSUSP, CDSUSP, CRPRNT, CFLUSH, CWERASE, CLNEXT};
135 (LCRTBS|LCRTERA|LCRTKIL|LCTLECH|LPENDIN|LDECCTQ);
138 strcpy(device, PTY_TEMPLATE); // string constants are not writable
139 blockLoc = &device[ strlen("/dev/pty") ];
140 numLoc = &device[ strlen("/dev/pty?") ];
141 msLoc = &device[ strlen("/dev/") ];
142 for (block = "pqrs"; *block; block++)
145 for (num = "0123456789abcdef"; *num; num++)
148 *master = open(device, O_RDWR);
152 *slave = open(device, O_RDWR);
155 (void) ioctl(*slave, TIOCSETP, (char *)&setp);
156 (void) ioctl(*slave, TIOCSETC, (char *)&setc);
157 (void) ioctl(*slave, TIOCSETD, (char *)&setd);
158 (void) ioctl(*slave, TIOCSLTC, (char *)&sltc);
159 (void) ioctl(*slave, TIOCLSET, (char *)&lset);
162 // close the master and reset the device
163 // name so that the master opens it properly
168 } /* hunting through a bank of ptys */
169 } /* hunting through blocks of ptys in all the right places */
175 @implementation Subprocess
177 /*==========================================================
179 * Public Instance Methods
181 *==========================================================*/
183 - init:(const char *)subprocessString
184 // a cover for the below withDelegate:nil, andPtySupport:NO, andStdErr:YES
188 init:subprocessString
194 - init:(const char *)subprocessString
195 withDelegate:theDelegate
196 andPtySupport:(BOOL)wantsPty
197 andStdErr:(BOOL)wantsStdErr
198 // initializes an instance of Subprocess and corresponding UNIX process
200 int pipeTo[2]; // for non-Pty support
202 int tty, numFds, fd; // for temporary use
204 int pidChild; // needed because childPid does not exist
205 // until Subprocess is instantiated
209 tty = open("/dev/tty", O_RDWR);
210 getptys(&masterPty,&slavePty);
211 if (masterPty <= 0 || slavePty <= 0)
213 showError("Error grabbing ptys for subprocess.", theDelegate);
216 // remove the controlling tty if launched from a shell,
217 // but not Workspace;
218 // so that we have job control over the parent application in shell
219 // and so that subprocesses can be restarted in Workspace
220 if ((tty<0) && ((tty = open("/dev/tty", 2))>=0))
222 ioctl(tty, TIOCNOTTY, 0);
228 if (pipe(pipeTo) < 0 || pipe(pipeFrom) < 0)
230 showError("Error starting UNIX pipes to subprocess.", theDelegate);
235 switch (pidChild = vfork())
238 showError("Error starting UNIX vfork of subprocess.", theDelegate);
252 dup2(pipeFrom[1], 1);
254 dup2(pipeFrom[1], 2);
257 numFds = getdtablesize();
258 for (fd=3; fd<numFds; fd++)
261 processGroup = getpid();
262 ioctl(0, TIOCSPGRP, (char *)&processGroup);
263 setpgrp (0, processGroup);
265 // we exec a /bin/sh so that cmds are easier to specify for the user
266 execl("/bin/sh", "sh", "-c", subprocessString, 0);
267 perror("vfork (child)"); // should never gets here tho
271 [self setDelegate:theDelegate];
278 fpToChild = fdopen(masterPty, "w");
279 fromChild = masterPty;
286 fpToChild = fdopen(pipeTo[1], "w");
287 fromChild = pipeFrom[0];
290 setbuf(fpToChild, NULL);
293 (DPSFDProc)fdHandler,
295 NX_MODALRESPTHRESHOLD+1);
300 - send:(const char *)string withNewline:(BOOL)wantNewline
302 fputs(string, fpToChild);
304 fputc('\n', fpToChild);
308 - send:(const char *)string
310 [self send:string withNewline:YES];
315 // effectively sends an EOF to the child process stdin
325 //kill(childPid+1, SIGTERM);
326 killpg(childPid, SIGTERM);
332 - setDelegate:anObject