|
|
1.1 root 1: /*
2: Subprocess.m (v10)
3: by Charles L. Oei
4: pty support by Joe Freeman
5: Subprocess Example, Release 2.0
6: NeXT Computer, Inc.
7:
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.
11: */
12:
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>
18:
19: #define PTY_TEMPLATE "/dev/pty??"
20: #define PTY_LENGTH 11
21:
22: static void showError();
23:
24:
25: /*==========================================================
26: *
27: * Private Instance Methods
28: *
29: *==========================================================*/
30:
31: @interface Subprocess(Private)
32: - childDidExit;
33: - fdHandler:(int)theFd;
34: @end
35:
36: @implementation Subprocess(Private)
37:
38: - childDidExit
39: // cleanup after a child process exits
40: {
41: if (childPid)
42: {
43: DPSRemoveFD(fromChild);
44: close(fromChild);
45: fclose(fpToChild);
46: childPid=0; // specify that child is dead
47: if (delegate && [delegate respondsTo:@selector(subprocessDone)])
48: [delegate perform:@selector(subprocessDone)];
49: }
50: return self;
51: }
52:
53: - fdHandler:(int)theFd
54: // DPS handler for output from subprocess
55: {
56: if (((bufferCount = read(theFd, outputBuffer, BUFFERSIZE-1)) < 0) ||
57: (!bufferCount))
58: {
59: [self childDidExit];
60: return self;
61: }
62: outputBuffer[bufferCount] = '\0';
63: if (delegate && [delegate respondsTo:@selector(subprocessOutput:)])
64: [delegate perform:@selector(subprocessOutput:)
65: with:(void *)&outputBuffer];
66: return self;
67: }
68:
69: @end
70:
71:
72: /*==========================================================
73: *
74: * Private Utility Routines
75: *
76: *==========================================================*/
77:
78: static void
79: showError (const char *errorString, id theDelegate)
80: // ensure errors never get dropped on the floor
81: {
82: if (theDelegate && [theDelegate respondsTo:@selector(subprocessError:)])
83: [theDelegate
84: perform:@selector(subprocessError:)
85: with:(void *)errorString];
86: else if (NXApp) // no delegate, but we're running w/in an App
87: NXRunAlertPanel(0, errorString, 0, 0, 0);
88: else
89: perror(errorString);
90: }
91:
92: static void
93: fdHandler (int theFd, id self)
94: // DPS handler for output from subprocess
95: {
96: [self fdHandler:theFd];
97: }
98:
99: static void
100: getptys (int *master, int *slave)
101: // attempt to setup the ptys
102: {
103: char device[PTY_LENGTH];
104: char *block, *num;
105: char *blockLoc; // specifies the location of block for the device string
106: char *numLoc; // specifies the pty name with the digit ptyxD
107: char *msLoc; // specifies the master (ptyxx) or slave (ttyxx)
108:
109: struct sgttyb setp =
110: {B9600, B9600, (char)0x7f, (char)0x15, (CRMOD|ANYP)};
111: struct tchars setc =
112: {CINTR, CQUIT, CSTART, CSTOP, CEOF, CBRK};
113: struct ltchars sltc =
114: {CSUSP, CDSUSP, CRPRNT, CFLUSH, CWERASE, CLNEXT};
115: int lset =
116: (LCRTBS|LCRTERA|LCRTKIL|LCTLECH|LPENDIN|LDECCTQ);
117: int setd = NTTYDISC;
118:
119: strcpy(device, PTY_TEMPLATE); // string constants are not writable
120: blockLoc = &device[ strlen("/dev/pty") ];
121: numLoc = &device[ strlen("/dev/pty?") ];
122: msLoc = &device[ strlen("/dev/") ];
123: for (block = "pqrs"; *block; block++)
124: {
125: *blockLoc = *block;
126: for (num = "0123456789abcdef"; *num; num++)
127: {
128: *numLoc = *num;
129: *master = open(device, O_RDWR);
130: if (*master >= 0)
131: {
132: *msLoc = 't';
133: *slave = open(device, O_RDWR);
134: if (*slave >= 0)
135: {
136: (void) ioctl(*slave, TIOCSETP, (char *)&setp);
137: (void) ioctl(*slave, TIOCSETC, (char *)&setc);
138: (void) ioctl(*slave, TIOCSETD, (char *)&setd);
139: (void) ioctl(*slave, TIOCSLTC, (char *)&sltc);
140: (void) ioctl(*slave, TIOCLSET, (char *)&lset);
141: return;
142: } else {
143: // close the master and reset the device
144: // name so that the master opens it properly
145: *msLoc = 'p';
146: close(*master);
147: }
148: }
149: } /* hunting through a bank of ptys */
150: } /* hunting through blocks of ptys in all the right places */
151: *master = -1;
152: *slave = -1;
153: }
154:
155:
156: @implementation Subprocess
157:
158: /*==========================================================
159: *
160: * Public Instance Methods
161: *
162: *==========================================================*/
163:
164: - init:(const char *)subprocessString
165: // a cover for the below withDelegate:nil, andPtySupport:NO, andStdErr:YES
166: {
167: return
168: [self
169: init:subprocessString
170: withDelegate:nil
171: andPtySupport:NO
172: andStdErr:YES];
173: }
174:
175: - init:(const char *)subprocessString
176: withDelegate:theDelegate
177: andPtySupport:(BOOL)wantsPty
178: andStdErr:(BOOL)wantsStdErr
179: // initializes an instance of Subprocess and corresponding UNIX process
180: {
181: int pipeTo[2]; // for non-Pty support
182: int pipeFrom[2];
183: int tty, numFds, fd; // for temporary use
184: int processGroup;
185: int pidChild; // needed because childPid does not exist
186: // until Subprocess is instantiated
187:
188: if (wantsPty)
189: {
190: tty = open("/dev/tty", O_RDWR);
191: getptys(&masterPty,&slavePty);
192: if (masterPty <= 0 || slavePty <= 0)
193: {
194: showError("Error grabbing ptys for subprocess.", theDelegate);
195: return self;
196: }
197: // remove the controlling tty if launched from a shell,
198: // but not Workspace;
199: // so that we have job control over the parent application in shell
200: // and so that subprocesses can be restarted in Workspace
201: if ((tty<0) && ((tty = open("/dev/tty", 2))>=0))
202: {
203: ioctl(tty, TIOCNOTTY, 0);
204: close(tty);
205: }
206: }
207: else
208: {
209: if (pipe(pipeTo) < 0 || pipe(pipeFrom) < 0)
210: {
211: showError("Error starting UNIX pipes to subprocess.", theDelegate);
212: return self;
213: }
214: }
215:
216: switch (pidChild = vfork())
217: {
218: case -1: // error
219: showError("Error starting UNIX vfork of subprocess.", theDelegate);
220: return self;
221:
222: case 0: // child
223: if (wantsPty)
224: {
225: dup2(slavePty, 0);
226: dup2(slavePty, 1);
227: if (wantsStdErr)
228: dup2(slavePty, 2);
229: }
230: else
231: {
232: dup2(pipeTo[0], 0);
233: dup2(pipeFrom[1], 1);
234: if (wantsStdErr)
235: dup2(pipeFrom[1], 2);
236: }
237:
238: numFds = getdtablesize();
239: for (fd=3; fd<numFds; fd++)
240: close(fd);
241:
242: processGroup = getpid();
243: ioctl(0, TIOCSPGRP, (char *)&processGroup);
244: setpgrp (0, processGroup);
245:
246: // we exec a /bin/sh so that cmds are easier to specify for the user
247: execl("/bin/sh", "sh", "-c", subprocessString, 0);
248: perror("vfork (child)"); // should never gets here tho
249: exit(1);
250:
251: default: // parent
252: [self setDelegate:theDelegate];
253: childPid = pidChild;
254:
255: if (wantsPty)
256: {
257: close(slavePty);
258:
259: fpToChild = fdopen(masterPty, "w");
260: fromChild = masterPty;
261: }
262: else
263: {
264: close(pipeTo[0]);
265: close(pipeFrom[1]);
266:
267: fpToChild = fdopen(pipeTo[1], "w");
268: fromChild = pipeFrom[0];
269: }
270:
271: setbuf(fpToChild, NULL);
272: DPSAddFD(
273: fromChild,
274: (DPSFDProc)fdHandler,
275: (id)self,
276: NX_MODALRESPTHRESHOLD+1);
277: return self;
278: }
279: }
280:
281: - send:(const char *)string withNewline:(BOOL)wantNewline
282: {
283: fputs(string, fpToChild);
284: if (wantNewline)
285: fputc('\n', fpToChild);
286: return self;
287: }
288:
289: - send:(const char *)string
290: {
291: [self send:string withNewline:YES];
292: return self;
293: }
294:
295: - terminateInput
296: // effectively sends an EOF to the child process stdin
297: {
298: fclose(fpToChild);
299: return self;
300: }
301:
302: - terminate:sender
303: {
304: if (childPid)
305: {
306: kill(childPid+1, SIGTERM);
307: [self childDidExit];
308: }
309: return self;
310: }
311:
312: - setDelegate:anObject
313: {
314: delegate = anObject;
315: return self;
316: }
317:
318: - delegate
319: {
320: return delegate;
321: }
322:
323: @end
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.