1 /* -*-C-*-
2 
3 Copyright (C) 1986, 1987, 1988, 1989, 1990, 1991, 1992, 1993, 1994,
4     1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
5     2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014 Massachusetts
6     Institute of Technology
7 
8 This file is part of MIT/GNU Scheme.
9 
10 MIT/GNU Scheme is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or (at
13 your option) any later version.
14 
15 MIT/GNU Scheme is distributed in the hope that it will be useful, but
16 WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18 General Public License for more details.
19 
20 You should have received a copy of the GNU General Public License
21 along with MIT/GNU Scheme; if not, write to the Free Software
22 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301,
23 USA.
24 
25 */
26 
27 #include "nt.h"
28 #include "ntproc.h"
29 #include "ntio.h"
30 #include "ntscreen.h"
31 #include "ntgui.h"
32 
33 extern const char * OS_working_dir_pathname (void);
34 
35 typedef struct
36 {
37   PROCESS_INFORMATION handles;
38   unsigned long tick;
39   unsigned long sync_tick;
40   DWORD raw_reason;
41   DWORD reason;
42   enum process_status raw_status;
43   enum process_status status;
44 } process_t;
45 #define _PROCESS(process) (process_table [(process)])
46 #define PROCESS_HANDLES(process) ((_PROCESS(process)) . handles)
47 #define PROCESS_TICK(process) ((_PROCESS(process)) . tick)
48 #define PROCESS_SYNC_TICK(process) ((_PROCESS(process)) . sync_tick)
49 #define PROCESS_RAW_REASON(process) ((_PROCESS(process)) . raw_reason)
50 #define PROCESS_REASON(process) ((_PROCESS(process)) . reason)
51 #define PROCESS_RAW_STATUS(process) ((_PROCESS(process)) . raw_status)
52 #define PROCESS_STATUS(process) ((_PROCESS(process)) . status)
53 
54 #define PROCESS_HANDLE(process) ((PROCESS_HANDLES (process)) . hProcess)
55 #define PROCESS_ID(process) ((PROCESS_HANDLES (process)) . dwProcessId)
56 
57 #ifndef NT_DEFAULT_PROCESS_TABLE_SIZE
58 #define NT_DEFAULT_PROCESS_TABLE_SIZE 4096
59 #endif
60 Tprocess OS_process_table_size;
61 enum process_jc_status scheme_jc_status;
62 
63 static process_t * process_table;
64 static unsigned long process_tick;
65 static unsigned long sync_tick;
66 
67 #undef USE_PROCESS_TABLE_LOCK
68 #ifdef USE_PROCESS_TABLE_LOCK
69 static CRITICAL_SECTION process_table_lock;
70 #define GRAB_PROCESS_TABLE() EnterCriticalSection (&process_table_lock)
71 #define RELEASE_PROCESS_TABLE() LeaveCriticalSection (&process_table_lock)
72 #else
73 #define GRAB_PROCESS_TABLE()
74 #define RELEASE_PROCESS_TABLE()
75 #endif
76 
77 #define PROCESS_STATUS_SYNC(process)					\
78 {									\
79   (PROCESS_STATUS (process)) = (PROCESS_RAW_STATUS (process));		\
80   (PROCESS_REASON (process)) = (PROCESS_RAW_REASON (process));		\
81   (PROCESS_SYNC_TICK (process)) = (PROCESS_TICK (process));		\
82 }
83 
84 /* I have no idea what a good value for this might be, so I've picked
85    a random non-zero value.  */
86 #define TERMINATE_PROCESS_EXIT_CODE 0xFF
87 
88 #undef TRACE_NTPROC
89 #ifdef TRACE_NTPROC
90 FILE * trace_file;
91 #ifndef TRACE_NTPROC_FILENAME
92 #define TRACE_NTPROC_FILENAME "nttrace.out"
93 #endif
94 #endif
95 
96 static void lock_process_table (void);
97 static void lock_process_table_1 (void *);
98 static HANDLE stdio_handle (DWORD, Tchannel, enum process_channel_type);
99 static HANDLE copy_handle (HANDLE);
100 static Tprocess allocate_process (void);
101 static void allocate_process_abort (void *);
102 static HWND find_child_console (DWORD);
103 static BOOL CALLBACK find_child_console_1 (HWND, LPARAM);
104 static void process_wait_1 (Tprocess, DWORD);
105 static void process_death (Tprocess);
106 
107 void
NT_initialize_processes(void)108 NT_initialize_processes (void)
109 {
110 #ifdef TRACE_NTPROC
111   trace_file = (fopen (TRACE_NTPROC_FILENAME, "w"));
112 #endif
113   OS_process_table_size = NT_DEFAULT_PROCESS_TABLE_SIZE;
114   process_table = (OS_malloc (OS_process_table_size * (sizeof (process_t))));
115   {
116     Tprocess process;
117     for (process = 0; (process < OS_process_table_size); process += 1)
118       OS_process_deallocate (process);
119   }
120 #ifdef USE_PROCESS_TABLE_LOCK
121   InitializeCriticalSection (&process_table_lock);
122 #endif
123   scheme_jc_status = process_jc_status_no_ctty;
124   process_tick = 0;
125   sync_tick = 0;
126 }
127 
128 static void
lock_process_table(void)129 lock_process_table (void)
130 {
131   GRAB_PROCESS_TABLE ();
132   transaction_record_action (tat_always, lock_process_table_1, 0);
133 }
134 
135 static void
lock_process_table_1(void * ignore)136 lock_process_table_1 (void * ignore)
137 {
138   RELEASE_PROCESS_TABLE ();
139 }
140 
141 Tprocess
NT_make_subprocess(const char * filename,const char * command_line,const char * environment,const char * working_directory,enum process_channel_type channel_in_type,Tchannel channel_in,enum process_channel_type channel_out_type,Tchannel channel_out,enum process_channel_type channel_err_type,Tchannel channel_err,int hide_windows_p)142 NT_make_subprocess (const char * filename,
143 		    const char * command_line,
144 		    const char * environment,
145 		    const char * working_directory,
146 		    enum process_channel_type channel_in_type,
147 		    Tchannel channel_in,
148 		    enum process_channel_type channel_out_type,
149 		    Tchannel channel_out,
150 		    enum process_channel_type channel_err_type,
151 		    Tchannel channel_err,
152 		    int hide_windows_p)
153 {
154   Tprocess child;
155   SECURITY_ATTRIBUTES sap;
156   SECURITY_DESCRIPTOR sdp;
157   STARTUPINFO si;
158 
159   transaction_begin ();
160 
161   /* Explicitly specify no security */
162   STD_BOOL_API_CALL
163     (InitializeSecurityDescriptor, ((&sdp), SECURITY_DESCRIPTOR_REVISION));
164   STD_BOOL_API_CALL (SetSecurityDescriptorDacl, ((&sdp), TRUE, 0, FALSE));
165   (sap . nLength) = (sizeof (sap));
166   (sap . lpSecurityDescriptor) = (&sdp);
167   (sap . bInheritHandle) = FALSE;
168 
169   memset ((&si), 0, (sizeof (si)));
170   (si . cb) = (sizeof (si));
171   /* Specify the handles to be used by the child process.  */
172   (si . dwFlags) = STARTF_USESTDHANDLES;
173   (si . hStdInput)
174     = (stdio_handle (STD_INPUT_HANDLE, channel_in, channel_in_type));
175   (si . hStdOutput)
176     = (stdio_handle (STD_OUTPUT_HANDLE, channel_out, channel_out_type));
177   (si . hStdError)
178     = (stdio_handle (STD_ERROR_HANDLE, channel_err, channel_err_type));
179   /* If requested, hide the top-level window of the child process.  */
180   if (hide_windows_p)
181     {
182       (si . dwFlags) |= STARTF_USESHOWWINDOW;
183       (si . wShowWindow) |= SW_HIDE;
184     }
185 
186   lock_process_table ();
187   child = (allocate_process ());
188   STD_BOOL_API_CALL
189     (CreateProcess,
190      (((LPCTSTR) filename),
191       ((LPSTR) command_line),
192       (&sap),
193       0,
194       TRUE,
195       (CREATE_DEFAULT_ERROR_MODE | CREATE_NEW_CONSOLE),
196       ((LPVOID) environment),
197       ((LPCTSTR) working_directory),
198       (&si),
199       (& (PROCESS_HANDLES (child)))));
200   (PROCESS_RAW_STATUS (child)) = process_status_running;
201   (PROCESS_RAW_REASON (child)) = STILL_ACTIVE;
202   (PROCESS_TICK (child)) = process_tick;
203   PROCESS_STATUS_SYNC (child);
204   STD_BOOL_API_CALL (CloseHandle, (si . hStdInput));
205   STD_BOOL_API_CALL (CloseHandle, (si . hStdOutput));
206   STD_BOOL_API_CALL (CloseHandle, (si . hStdError));
207   transaction_commit ();
208   STD_BOOL_API_CALL (CloseHandle, ((PROCESS_HANDLES (child)) . hThread));
209   ((PROCESS_HANDLES (child)) . hThread) = INVALID_HANDLE_VALUE;
210 #ifdef TRACE_NTPROC
211   fprintf (trace_file, "Subprocess created: id=0x%x\n", (PROCESS_ID (child)));
212   fflush (trace_file);
213 #endif
214   return (child);
215 }
216 
217 static HANDLE
stdio_handle(DWORD target,Tchannel channel,enum process_channel_type type)218 stdio_handle (DWORD target, Tchannel channel, enum process_channel_type type)
219 {
220   return
221     (copy_handle ((type == process_channel_type_explicit)
222 		  ? (CHANNEL_HANDLE (channel))
223 		  : (GetStdHandle (target))));
224 }
225 
226 static HANDLE
copy_handle(HANDLE handle)227 copy_handle (HANDLE handle)
228 {
229   HANDLE parent = (GetCurrentProcess ());
230   HANDLE copy;
231   STD_BOOL_API_CALL
232     (DuplicateHandle,
233      (parent, handle, parent, (&copy), 0, TRUE, DUPLICATE_SAME_ACCESS));
234   NT_handle_close_on_abort (copy);
235   return (copy);
236 }
237 
238 static Tprocess
allocate_process(void)239 allocate_process (void)
240 {
241   unsigned int process;
242   for (process = 0; (process < OS_process_table_size); process += 1)
243     if ((PROCESS_RAW_STATUS (process)) == process_status_free)
244       {
245 	Tprocess * pp = (dstack_alloc (sizeof (Tprocess)));
246 	(*pp) = process;
247 	transaction_record_action (tat_abort, allocate_process_abort, pp);
248 	(PROCESS_RAW_STATUS (process)) = process_status_allocated;
249 	return (process);
250       }
251   error_out_of_processes ();
252   return (NO_PROCESS);
253 }
254 
255 static void
allocate_process_abort(void * environment)256 allocate_process_abort (void * environment)
257 {
258   Tprocess process = (* ((Tprocess *) environment));
259   OS_process_deallocate (process);
260 }
261 
262 struct fcc_info
263 {
264   DWORD pid;
265   HWND hwnd;
266 };
267 
268 static HWND
find_child_console(DWORD pid)269 find_child_console (DWORD pid)
270 {
271   struct fcc_info fi;
272   (fi . pid) = pid;
273   (fi . hwnd) = INVALID_HANDLE_VALUE;
274   EnumWindows (find_child_console_1, ((LPARAM) (&fi)));
275   return (fi . hwnd);
276 }
277 
278 static BOOL CALLBACK
find_child_console_1(HWND hwnd,LPARAM lfi)279 find_child_console_1 (HWND hwnd, LPARAM lfi)
280 {
281   struct fcc_info * fi = ((struct fcc_info *) lfi);
282   DWORD pid;
283 
284   (void) GetWindowThreadProcessId (hwnd, (&pid));
285   if (pid == (fi -> pid))
286     {
287       char window_class [32];
288       unsigned int n
289 	= (GetClassName (hwnd, window_class, (sizeof (window_class))));
290       const char * console_class
291 	= ((NT_windows_type == wintype_95) ? "tty" : "ConsoleWindowClass");
292       if ((n == (strlen (console_class)))
293 	  && ((strcmp (window_class, console_class)) == 0))
294 	{
295 	  (fi -> hwnd) = hwnd;
296 	  return (FALSE);
297 	}
298     }
299   /* keep looking */
300   return (TRUE);
301 }
302 
303 void
OS_process_deallocate(Tprocess process)304 OS_process_deallocate (Tprocess process)
305 {
306   (PROCESS_RAW_STATUS (process)) = process_status_free;
307 }
308 
309 int
OS_process_valid_p(Tprocess process)310 OS_process_valid_p (Tprocess process)
311 {
312   if (process > OS_process_table_size)
313     return (0);
314   switch (PROCESS_RAW_STATUS (process))
315     {
316     case process_status_exited:
317     case process_status_signalled:
318     case process_status_running:
319       return (1);
320     default:
321       return (0);
322     }
323 }
324 
325 int
OS_process_continuable_p(Tprocess process)326 OS_process_continuable_p (Tprocess process)
327 {
328   return ((PROCESS_RAW_STATUS (process)) == process_status_running);
329 }
330 
331 int
OS_process_foregroundable_p(Tprocess process)332 OS_process_foregroundable_p (Tprocess process)
333 {
334   return (0);
335 }
336 
337 pid_t
OS_process_id(Tprocess process)338 OS_process_id (Tprocess process)
339 {
340   return (PROCESS_ID (process));
341 }
342 
343 enum process_jc_status
OS_process_jc_status(Tprocess process)344 OS_process_jc_status (Tprocess process)
345 {
346   return (process_jc_status_no_ctty);
347 }
348 
349 int
OS_process_status_sync(Tprocess process)350 OS_process_status_sync (Tprocess process)
351 {
352 #ifdef TRACE_NTPROC
353   fprintf (trace_file, "OS_process_status_sync: id=0x%x\n",
354 	   (PROCESS_ID (process)));
355   fflush (trace_file);
356 #endif
357   transaction_begin ();
358   lock_process_table ();
359   {
360     int result = ((PROCESS_TICK (process)) != (PROCESS_SYNC_TICK (process)));
361     if (result)
362       {
363 #ifdef TRACE_NTPROC
364 	fprintf (trace_file, "(status=0x%x raw_status=0x%x)\n",
365 		 (PROCESS_STATUS (process)),
366 		 (PROCESS_RAW_STATUS (process)));
367 	fflush (trace_file);
368 #endif
369 	PROCESS_STATUS_SYNC (process);
370       }
371     transaction_commit ();
372     return (result);
373   }
374 }
375 
376 int
OS_process_status_sync_all(void)377 OS_process_status_sync_all (void)
378 {
379 #ifdef TRACE_NTPROC
380   fprintf (trace_file, "OS_process_status_sync_all\n");
381   fflush (trace_file);
382 #endif
383   transaction_begin ();
384   lock_process_table ();
385   {
386     int result = (process_tick != sync_tick);
387     if (result)
388       {
389 #ifdef TRACE_NTPROC
390 	fprintf (trace_file, "(status change)\n");
391 	fflush (trace_file);
392 #endif
393 	sync_tick = process_tick;
394       }
395     transaction_commit ();
396     return (result);
397   }
398 }
399 
400 enum process_status
OS_process_status(Tprocess process)401 OS_process_status (Tprocess process)
402 {
403 #ifdef TRACE_NTPROC
404   fprintf (trace_file, "OS_process_status: id=0x%x status=0x%x\n",
405 	   (PROCESS_ID (process)),
406 	   (PROCESS_STATUS (process)));
407   fflush (trace_file);
408 #endif
409   return (PROCESS_STATUS (process));
410 }
411 
412 unsigned short
OS_process_reason(Tprocess process)413 OS_process_reason (Tprocess process)
414 {
415   return ((unsigned short) (PROCESS_REASON (process)));
416 }
417 
418 void
OS_process_send_signal(Tprocess process,int sig)419 OS_process_send_signal (Tprocess process, int sig)
420 {
421   error_unimplemented_primitive ();
422 }
423 
424 void
OS_process_kill(Tprocess process)425 OS_process_kill (Tprocess process)
426 {
427 #ifdef TRACE_NTPROC
428   fprintf (trace_file, "OS_process_kill: id=0x%x\n", (PROCESS_ID (process)));
429   fflush (trace_file);
430 #endif
431   if (NT_windows_type == wintype_nt)
432     {
433       HWND hwnd = (find_child_console (PROCESS_ID (process)));
434       if (hwnd != INVALID_HANDLE_VALUE)
435 	{
436 	  PostMessage (hwnd, WM_CLOSE, 0, 0);
437 	  return;
438 	}
439     }
440 #ifdef TRACE_NTPROC
441   fprintf (trace_file, "(using TerminateProcess)\n");
442   fflush (trace_file);
443 #endif
444   if (!TerminateProcess ((PROCESS_HANDLE (process)),
445 			 TERMINATE_PROCESS_EXIT_CODE))
446     {
447       DWORD code = (GetLastError ());
448       if (code != ERROR_ACCESS_DENIED)
449 	NT_error_api_call ((GetLastError ()), apicall_TerminateProcess);
450 #ifdef TRACE_NTPROC
451       fprintf (trace_file, "(ERROR_ACCESS_DENIED)\n");
452       fflush (trace_file);
453 #endif
454     }
455 }
456 
457 void
OS_process_stop(Tprocess process)458 OS_process_stop (Tprocess process)
459 {
460   error_unimplemented_primitive ();
461 }
462 
463 void
OS_process_interrupt(Tprocess process)464 OS_process_interrupt (Tprocess process)
465 {
466   HWND hwnd;
467   BYTE control_scan_code;
468   BYTE vk_break_code;
469   BYTE break_scan_code;
470   /* BYTE keyboard_state [256]; */
471   HWND foreground_window;
472 
473 #ifdef TRACE_NTPROC
474   fprintf (trace_file, "OS_process_interrupt: id=0x%x\n",
475 	   (PROCESS_ID (process)));
476   fflush (trace_file);
477 #endif
478   hwnd = (find_child_console (PROCESS_ID (process)));
479   if (hwnd == INVALID_HANDLE_VALUE)
480     return;
481   control_scan_code = ((BYTE) (MapVirtualKey (VK_CONTROL, 0)));
482   vk_break_code = VK_CANCEL;
483   break_scan_code = ((BYTE) (MapVirtualKey (vk_break_code, 0)));
484   if (break_scan_code == 0)
485     {
486       /* Fake Ctrl-C if we can't manage Ctrl-Break. */
487       vk_break_code = 'C';
488       break_scan_code = ((BYTE) (MapVirtualKey (vk_break_code, 0)));
489     }
490   /* STD_BOOL_API_CALL (GetKeyboardState, (keyboard_state)); */
491   foreground_window = (GetForegroundWindow ());
492   if (SetForegroundWindow (hwnd))
493     {
494       /* Generate keystrokes as if user had typed Ctrl-Break or Ctrl-C.  */
495       keybd_event (VK_CONTROL, control_scan_code, 0, 0);
496       keybd_event (vk_break_code, break_scan_code, 0, 0);
497       keybd_event (vk_break_code, break_scan_code, KEYEVENTF_KEYUP, 0);
498       keybd_event (VK_CONTROL, control_scan_code, KEYEVENTF_KEYUP, 0);
499       if (foreground_window)
500 	(void) SetForegroundWindow (foreground_window);
501     }
502   /* STD_BOOL_API_CALL (SetKeyboardState, (keyboard_state)); */
503 }
504 
505 void
OS_process_quit(Tprocess process)506 OS_process_quit (Tprocess process)
507 {
508   error_unimplemented_primitive ();
509 }
510 
511 void
OS_process_hangup(Tprocess process)512 OS_process_hangup (Tprocess process)
513 {
514   /* Edwin assumes that this primitive works.  Under unix, the default
515      behavior of SIGHUP is to kill the process, so we will emulate
516      SIGHUP by killing the process.  */
517   OS_process_kill (process);
518 }
519 
520 void
OS_process_continue_background(Tprocess process)521 OS_process_continue_background (Tprocess process)
522 {
523   /* A no-op, this should only be called when OS_process_continuable_p
524      is true, i.e. when the process is already running.  */
525 }
526 
527 void
OS_process_continue_foreground(Tprocess process)528 OS_process_continue_foreground (Tprocess process)
529 {
530   error_unimplemented_primitive ();
531 }
532 
533 #ifndef WIN32_WAIT_INTERVAL
534 #define WIN32_WAIT_INTERVAL 50
535 #endif
536 
537 void
OS_process_wait(Tprocess process)538 OS_process_wait (Tprocess process)
539 {
540   process_wait_1 (process, 0);
541   while (1)
542     {
543       if (((PROCESS_RAW_STATUS (process)) != process_status_running)
544 	  || (pending_interrupts_p ()))
545 	break;
546       process_wait_1 (process, WIN32_WAIT_INTERVAL);
547     }
548 }
549 
550 static void
process_wait_1(Tprocess process,DWORD interval)551 process_wait_1 (Tprocess process, DWORD interval)
552 {
553 #ifdef TRACE_NTPROC
554   fprintf (trace_file, "process_wait_1: id=0x%x raw_status=0x%x\n",
555 	   (PROCESS_ID (process)),
556 	   (PROCESS_RAW_STATUS (process)));
557   fflush (trace_file);
558 #endif
559   if ((PROCESS_RAW_STATUS (process)) == process_status_running)
560     {
561       DWORD code
562 	= (MsgWaitForMultipleObjects (1,
563 				      (& (PROCESS_HANDLE (process))),
564 				      FALSE,
565 				      interval,
566 				      QS_ALLINPUT));
567 #ifdef TRACE_NTPROC
568       fprintf (trace_file, "(wait result = 0x%x)\n", code);
569       fflush (trace_file);
570 #endif
571       switch (code)
572 	{
573 	case WAIT_OBJECT_0:
574 	  process_death (process);
575 	  break;
576 	case WAIT_FAILED:
577 	  NT_error_api_call ((GetLastError ()),
578 			     apicall_MsgWaitForMultipleObjects);
579 	  break;
580 	}
581     }
582 }
583 
584 int
OS_process_any_status_change(void)585 OS_process_any_status_change (void)
586 {
587   Tprocess process;
588 #ifdef TRACE_NTPROC
589   fprintf (trace_file, "OS_process_any_status_change\n");
590   fflush (trace_file);
591 #endif
592   for (process = 0; (process < OS_process_table_size); process += 1)
593     if ((PROCESS_RAW_STATUS (process)) == process_status_running)
594       switch (WaitForSingleObject ((PROCESS_HANDLE (process)), 0))
595 	{
596 	case WAIT_OBJECT_0:
597 	  process_death (process);
598 	  break;
599 	case WAIT_FAILED:
600 	  NT_error_api_call ((GetLastError ()),
601 			     apicall_MsgWaitForMultipleObjects);
602 	  break;
603 	}
604 #ifdef TRACE_NTPROC
605   if (process_tick != sync_tick)
606     {
607       fprintf (trace_file, "(status change)\n");
608       fflush (trace_file);
609     }
610 #endif
611   return (process_tick != sync_tick);
612 }
613 
614 static void
process_death(Tprocess process)615 process_death (Tprocess process)
616 {
617   DWORD exit_code;
618 #ifdef TRACE_NTPROC
619   fprintf (trace_file, "process_death: id=0x%x\n", (PROCESS_ID (process)));
620   fflush (trace_file);
621 #endif
622   STD_BOOL_API_CALL
623     (GetExitCodeProcess, ((PROCESS_HANDLE (process)), (&exit_code)));
624 #ifdef TRACE_NTPROC
625   fprintf (trace_file, "(exit_code = 0x%x)\n", exit_code);
626   fflush (trace_file);
627 #endif
628   GRAB_PROCESS_TABLE ();
629   (PROCESS_RAW_STATUS (process))
630     = ((exit_code == STATUS_CONTROL_C_EXIT)
631        ? process_status_signalled
632        : process_status_exited);
633   (PROCESS_RAW_REASON (process)) = exit_code;
634   (PROCESS_TICK (process)) = (++process_tick);
635   STD_BOOL_API_CALL (CloseHandle, (PROCESS_HANDLE (process)));
636   (PROCESS_HANDLE (process)) = INVALID_HANDLE_VALUE;
637   RELEASE_PROCESS_TABLE ();
638 }
639 
640 Tprocess
OS_make_subprocess(const char * filename,const char ** argv,const char ** envp,const char * working_directory,enum process_ctty_type ctty_type,char * ctty_name,enum process_channel_type channel_in_type,Tchannel channel_in,enum process_channel_type channel_out_type,Tchannel channel_out,enum process_channel_type channel_err_type,Tchannel channel_err)641 OS_make_subprocess (const char * filename,
642 		    const char ** argv,
643 		    const char ** envp,
644 		    const char * working_directory,
645 		    enum process_ctty_type ctty_type,
646 		    char * ctty_name,
647 		    enum process_channel_type channel_in_type,
648 		    Tchannel channel_in,
649 		    enum process_channel_type channel_out_type,
650 		    Tchannel channel_out,
651 		    enum process_channel_type channel_err_type,
652 		    Tchannel channel_err)
653 {
654   error_unimplemented_primitive ();
655   return (NO_PROCESS);
656 }
657