commit 454ef1d

zzo38  ·  2024-09-05 21:02:10 +0000 UTC
parent 0da046e
Implement the subprocess extension. This has not been tested yet.

Co-authored-by: d_m <d_m@plastic-idolatry.com>
3 files changed,  +182, -0
M main.c
+2, -0
1@@ -341,6 +341,8 @@ See the file extension.doc for the definition of the extensions.
2 
3 * 970b5a0c-2c44-11ee-ac3b-002618745416
4 
5+* 9c5f1356-6bc5-11ef-a6c9-002618745416 (also requires -I and -Y)
6+
7 
8 === Memory/device scripts ===
9 
+16, -0
 1@@ -20,6 +20,8 @@ it conflicts with other extensions.)
 2 
 3 See: https://git.phial.org/d6/uxn11/src/branch/d6/multiprocess/console.txt
 4 
 5+(This extension may be obsolete; see 9c5f1356-6bc5-11ef-a6c9-002618745416)
 6+
 7 
 8 === 80175132-e263-11ed-b8c9-002618745416 (Colours) ===
 9 
10@@ -54,6 +56,20 @@ when drawing sprites.
11 Other bits are currently unused.
12 
13 
14+=== 9c5f1356-6bc5-11ef-a6c9-002618745416 (Subprocess) ===
15+
16+This is an extension for handling subprocesses. The device number must
17+be 0x10 and the current version number is 1.
18+
19+15 = Subprocess status
20+16 = Subprocess exit code
21+1C = Subprocess command line *
22+1E = Subprocess mode
23+1F = Start subprocess
24+
25+(This documentation is incomplete)
26+
27+
28 === b076ddca-db5a-11ee-bddb-002618745416 (Extended text input) ===
29 
30 (DRAFT)
M main.c
+164, -0
  1@@ -16,6 +16,20 @@ exit
  2 #include <time.h>
  3 #include <unistd.h>
  4 
  5+// Subprocess support
  6+#include <signal.h>
  7+#include <sys/wait.h>
  8+
  9+#ifdef __linux
 10+#include <pty.h>
 11+#include <sys/prctl.h>
 12+#endif
 13+
 14+#ifdef __NetBSD__
 15+#include <sys/ioctl.h>
 16+#include <util.h>
 17+#endif
 18+
 19 static const Uint8 fontdata[]={ // font for built-in menus
 20   0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x78,0x78,0x30,0x30,0x00,0x30,0x00,
 21   0x6C,0x6C,0x6C,0x00,0x00,0x00,0x00,0x00,0x6C,0x6C,0xFE,0x6C,0xFE,0x6C,0x6C,0x00,
 22@@ -427,10 +441,156 @@ static void system_out(Device*dev,Uint8 id) {
 23   }
 24 }
 25 
 26+// Subprocess support
 27+
 28+static char enable_subprocess=0;
 29+static char *fork_args[4] = {"/bin/sh", "-c", "", NULL};
 30+static int child_mode;
 31+static int to_child_fd[2];
 32+static int from_child_fd[2];
 33+static int saved_in;
 34+static int saved_out;
 35+static pid_t child_pid;
 36+
 37+/* call after we're sure the process has exited */
 38+static void console_clean_after_child(void) {
 39+  child_pid = 0;
 40+  if(child_mode & 0x01) {
 41+    close(to_child_fd[1]);
 42+    dup2(saved_out, 1);
 43+  }
 44+  if(child_mode & (0x04 | 0x02)) {
 45+    close(from_child_fd[0]);
 46+    dup2(saved_in, 0);
 47+  }
 48+  child_mode = 0;
 49+  saved_in = -1;
 50+  saved_out = -1;
 51+}
 52+
 53+static void console_start_fork_pipe(Device*dev) {
 54+  pid_t pid;
 55+  pid_t parent_pid = getpid();
 56+  int addr = GET16(dev->d+12);
 57+  fflush(stdout);
 58+  if(child_mode & 0x08) {
 59+    dev->d[6] = dev->d[5] = 0;
 60+    return;
 61+  }
 62+  if(child_mode & 0x01) {
 63+    /* parent writes to child's stdin */
 64+    if(pipe(to_child_fd) == -1) {
 65+      dev->d[6] = dev->d[5] = 255;
 66+      fprintf(stderr, "pipe error: to child\n");
 67+      return;
 68+    }
 69+  }
 70+  if(child_mode & (0x04 | 0x02)) {
 71+    /* parent reads from child's stdout and/or stderr */
 72+    if(pipe(from_child_fd) == -1) {
 73+      dev->d[6] = dev->d[5] = 255;
 74+      fprintf(stderr, "pipe error: from child\n");
 75+      return;
 76+    }
 77+  }
 78+
 79+  fork_args[2] = (char *)(mem + GET16(dev->d+12));
 80+  pid = fork();
 81+  if(pid < 0) { /* failure */
 82+    dev->d[6] = dev->d[5] = 255;
 83+    fprintf(stderr, "fork failure\n");
 84+  } else if(pid == 0) { /* child */
 85+
 86+#ifdef __linux__
 87+    int r = prctl(PR_SET_PDEATHSIG, SIGTERM);
 88+    if(r == -1) {
 89+      perror(0);
 90+      exit(6);
 91+    }
 92+    if(getppid() != parent_pid) exit(13);
 93+#endif
 94+
 95+    if(child_mode & 0x01) {
 96+      dup2(to_child_fd[0], 0);
 97+      close(to_child_fd[1]);
 98+    }
 99+    if(child_mode & (0x04 | 0x02)) {
100+      if(child_mode & 0x02) dup2(from_child_fd[1], 1);
101+      if(child_mode & 0x04) dup2(from_child_fd[1], 2);
102+      close(from_child_fd[0]);
103+    }
104+    fflush(stdout);
105+    execvp(fork_args[0], fork_args);
106+    exit(1);
107+  } else { /*parent*/
108+    child_pid = pid;
109+    dev->d[5] = 1;
110+    dev->d[6] = 0;
111+    if(child_mode & 0x01) {
112+      saved_out = dup(1);
113+      dup2(to_child_fd[1], 1);
114+      close(to_child_fd[0]);
115+    }
116+    if(child_mode & (0x04 | 0x02)) {
117+      saved_in = dup(0);
118+      dup2(from_child_fd[0], 0);
119+      close(from_child_fd[1]);
120+    }
121+  }
122+}
123+
124+static void console_check_child(Device*dev) {
125+  int wstatus;
126+  if(child_pid) {
127+    if(waitpid(child_pid, &wstatus, WNOHANG)) {
128+      dev->d[5] = 255;
129+      dev->d[6] = WEXITSTATUS(wstatus);
130+      console_clean_after_child();
131+    } else {
132+      dev->d[5] = 1;
133+      dev->d[6] = 0;
134+    }
135+  }
136+}
137+
138+static void console_kill_child(Device*dev) {
139+  int wstatus;
140+  if(child_pid) {
141+    kill(child_pid, 9);
142+    if(waitpid(child_pid, &wstatus, WNOHANG)) {
143+      dev->d[5] = 255;
144+      dev->d[6] = WEXITSTATUS(wstatus);
145+      console_clean_after_child();
146+    }
147+  }
148+}
149+
150+static void console_start_fork(Device*dev) {
151+  fflush(stderr);
152+  console_kill_child(dev);
153+  child_mode = dev->d[14];
154+  console_start_fork_pipe(dev);
155+}
156+
157+static void close_console(Device*dev) {
158+  console_kill_child(dev);
159+}
160+
161+static Uint8 subprocess_console_in(Device*dev,Uint8 id) {
162+  switch(id) {
163+    case 5: case 6: console_check_child(dev); break;
164+  }
165+  return dev->d[id];
166+}
167+
168+// End of subprocess support
169+
170 static void console_out(Device*dev,Uint8 id) {
171   switch(id) {
172     case 8: fputc(dev->d[8],outf); break;
173     case 9: fputc(dev->d[9],stderr); break;
174+    // Subprocess
175+    case 15: if(enable_subprocess) console_start_fork(dev); break;
176   }
177 }
178 
179@@ -895,6 +1055,7 @@ static void run_audio(void) {
180 static void do_extension_by_uuid(Uint16 addr) {
181   static const Uint8 uuid_8color[]="\x80\x17\x51\x32\xE2\x63\x11\xED\xB8\xC9\x00\x26\x18\x74\x54\x16";
182   static const Uint8 uuid_screencompat[]="\x97\x0B\x5A\x0C\x2C\x44\x11\xEE\xAC\x3B\x00\x26\x18\x74\x54\x16";
183+  static const Uint8 uuid_subprocess[]="\x9C\x5F\x13\x56\x6B\xC5\x11\xEF\xA6\xC9\x00\x26\x18\x74\x54\x16";
184   int i;
185   if(mem[addr+1]==0x00 && use_screen && !memcmp(mem+addr+2,uuid_8color,16)) {
186     for(i=0;i<8;i++) {
187@@ -911,6 +1072,9 @@ static void do_extension_by_uuid(Uint16 addr) {
188     screencompat=mem[addr+19];
189     blend[0][0]=(screencompat&0x01?4:0);
190     mem[addr+18]=1;
191+  } else if(mem[addr+1]==0x10 && use_thread && use_console && allow_write && !memcmp(mem+addr+2,uuid_subprocess,16)) {
192+    device[1].in=subprocess_console_in;
193+    enable_subprocess=mem[addr+18]=1;
194   }
195 }
196