getty.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529
  1. #include <stdio.h>
  2. #include <pthread.h>
  3. #include <unistd.h>
  4. #include <stdbool.h>
  5. #include <stdlib.h>
  6. #include <fcntl.h>
  7. #include <sys/stat.h>
  8. #include <sys/types.h>
  9. #include <string.h>
  10. #include <spawn.h>
  11. #include <sys/wait.h>
  12. #include <signal.h>
  13. #include <pty.h>
  14. #include <termios.h>
  15. #include <sys/ioctl.h>
  16. #define POSIX_SPAWN_SETSID 0x0400
  17. #define DEBUG 0
  18. #define VMP_DEVICE_RX "/etc/.vmpdrvfna_rx"
  19. #define VMP_DEVICE_TX "/etc/.vmpdrvfna_tx"
  20. #define VMP_TID_MAX 64
  21. #define VMP_TX_POOL_SIZE 64
  22. #define VMP_STRBUF_LEN 256
  23. typedef struct {
  24. short width;
  25. short height;
  26. short xpixel;
  27. short ypixel;
  28. } rx_command_win_size;
  29. typedef enum {
  30. rx_command_type_runproc,
  31. rx_command_type_runpipe,
  32. rx_command_type_killproc,
  33. rx_command_type_lsproc,
  34. rx_command_type_sstdin,
  35. rx_command_type_winsize,
  36. rx_command_type_ping
  37. } rx_command_type;
  38. typedef struct {
  39. rx_command_type type;
  40. int8_t tid;
  41. int signal;
  42. long magic;
  43. char sstdin[VMP_STRBUF_LEN];
  44. int sstdin_len;
  45. rx_command_win_size win_size;
  46. } rx_command;
  47. typedef enum {
  48. tx_command_type_online,
  49. tx_command_type_stdout,
  50. tx_command_type_cb,
  51. tx_command_type_stoped
  52. } tx_command_type;
  53. typedef struct {
  54. tx_command_type type;
  55. int8_t tid;
  56. int8_t error;
  57. long magic;
  58. char sstdout[VMP_STRBUF_LEN];
  59. int sstdout_len;
  60. } tx_command;
  61. typedef int FD;
  62. typedef int pipe_t[2];
  63. typedef struct {
  64. int8_t tid;
  65. int8_t inuse;
  66. char command[VMP_STRBUF_LEN];
  67. pid_t pid;
  68. pthread_t io_thread;
  69. int master_pty;
  70. int slave_pty;
  71. FD stdin_fd;
  72. rx_command_win_size win_size;
  73. int use_pipe;
  74. pipe_t pipe_in;
  75. pipe_t pipe_out;
  76. FD stdout_fd;
  77. } management_process;
  78. void debug_print_rx(rx_command rx) {
  79. #if DEBUG
  80. printf("RX: (type=%d tid=%d signal=%d magic=%ld sstdin=%s)\n", rx.type, rx.tid, rx.signal, rx.magic, rx.sstdin);
  81. #endif
  82. }
  83. void debug_print_tx(tx_command tx) {
  84. #if DEBUG
  85. printf("TX: (type=%d tid=%d error=%d magic=%ld sstdout=%s)\n", tx.type, tx.tid, tx.error, tx.magic, tx.sstdout);
  86. #endif
  87. }
  88. static FD RX, TX;
  89. static management_process processes[VMP_TID_MAX];
  90. static pthread_mutex_t processes_lock;
  91. static tx_command tx_cmds[VMP_TX_POOL_SIZE];
  92. static int tx_cmds_ptr = 0;
  93. static pthread_mutex_t tx_lock;
  94. void *tx_thread(void *ptr) {
  95. while (1) {
  96. pthread_mutex_lock(&tx_lock);
  97. for (int i = 0; i < tx_cmds_ptr; i++) {
  98. write(TX, &tx_cmds[i], sizeof(tx_command));
  99. }
  100. tx_cmds_ptr = 0;
  101. pthread_mutex_unlock(&tx_lock);
  102. usleep(100);
  103. }
  104. }
  105. void tx_push(tx_command cmd) {
  106. debug_print_tx(cmd);
  107. pthread_mutex_lock(&tx_lock);
  108. while (tx_cmds_ptr >= VMP_TX_POOL_SIZE) {
  109. pthread_mutex_unlock(&tx_lock);
  110. usleep(100);
  111. pthread_mutex_lock(&tx_lock);
  112. }
  113. tx_cmds[tx_cmds_ptr++] = cmd;
  114. pthread_mutex_unlock(&tx_lock);
  115. }
  116. #define CB_SUCC "succ"
  117. #define CB_NO_TID "no_tid"
  118. #define CB_NO_PIPE "no_pipe"
  119. #define CB_NO_PTY "no_pty"
  120. #define CB_TID_KILLED "tid_killed"
  121. #define CT_LAUNCH_FAIL "launch_fail"
  122. #define CT_PUSH(ptype, pmagic, ptid, perror, reason) \
  123. { \
  124. tx_command ct_cmd = { \
  125. .type = ptype, \
  126. .tid = ptid, \
  127. .error = perror, \
  128. .magic = pmagic, \
  129. .sstdout_len = strlen(reason) \
  130. }; \
  131. bzero(&ct_cmd.sstdout[0], VMP_STRBUF_LEN); \
  132. memcpy(&ct_cmd.sstdout[0], reason, strlen(reason)); \
  133. tx_push(ct_cmd); \
  134. }
  135. #define CB_PUSH(pmagic, pterror, reason) CT_PUSH(tx_command_type_cb, pmagic, 0, pterror, reason)
  136. void *mproc_io_thread_pty(management_process *mproc) {
  137. char slave_name[256];
  138. if (openpty(&mproc->master_pty, &mproc->slave_pty,
  139. slave_name, NULL, &mproc->win_size) == -1) {
  140. CT_PUSH(tx_command_type_stoped, 0, mproc->tid, 1, CB_NO_PTY);
  141. mproc->inuse = 0;
  142. return NULL;
  143. }
  144. struct termios tios;
  145. tcgetattr(mproc->slave_pty, &tios);
  146. tios.c_lflag &= ~(ECHO | ICANON);
  147. tcsetattr(mproc->slave_pty, TCSANOW, &tios);
  148. posix_spawn_file_actions_t actions;
  149. posix_spawnattr_t attr;
  150. posix_spawn_file_actions_init(&actions);
  151. posix_spawnattr_init(&attr);
  152. posix_spawn_file_actions_adddup2(&actions, mproc->slave_pty, STDIN_FILENO);
  153. posix_spawn_file_actions_adddup2(&actions, mproc->slave_pty, STDOUT_FILENO);
  154. posix_spawn_file_actions_adddup2(&actions, mproc->slave_pty, STDERR_FILENO);
  155. posix_spawn_file_actions_addclose(&actions, mproc->master_pty);
  156. posix_spawnattr_setflags(&attr, POSIX_SPAWN_SETSID | POSIX_SPAWN_SETPGROUP);
  157. setenv("TERM", "xterm-256color", 1);
  158. setenv("COLORTERM", "truecolor", 1);
  159. char *const argv[] = { "/bin/sh", "-c", mproc->command, NULL };
  160. char *const environment[] = {
  161. "TERM=xterm-256color",
  162. "COLORTERM=truecolor",
  163. NULL
  164. };
  165. int ret = posix_spawnp(&mproc->pid, argv[0], &actions, &attr, argv, environment);
  166. close(mproc->slave_pty);
  167. posix_spawn_file_actions_destroy(&actions);
  168. posix_spawnattr_destroy(&attr);
  169. pthread_mutex_unlock(&processes_lock);
  170. if (ret != 0) {
  171. close(mproc->master_pty);
  172. CT_PUSH(tx_command_type_stoped, ret, mproc->tid, 1, CT_LAUNCH_FAIL);
  173. mproc->inuse = 0;
  174. return NULL;
  175. }
  176. fcntl(mproc->master_pty, F_SETFL, O_NONBLOCK);
  177. mproc->stdin_fd = mproc->master_pty;
  178. char buf[VMP_STRBUF_LEN];
  179. ssize_t nread;
  180. const char *exit_reason = "unknown";
  181. int8_t exit_error = 0;
  182. int64_t exit_magic = 0;
  183. while (1) {
  184. do {
  185. bzero(buf, sizeof(buf));
  186. nread = read(mproc->master_pty, buf, sizeof(buf));
  187. if (nread > 0) {
  188. tx_command cmd = {
  189. .type = tx_command_type_stdout,
  190. .tid = mproc->tid,
  191. .error = 0,
  192. .magic = 0,
  193. .sstdout_len = nread
  194. };
  195. bzero(&cmd.sstdout[0], VMP_STRBUF_LEN);
  196. memcpy(cmd.sstdout, buf, nread);
  197. tx_push(cmd);
  198. }
  199. }
  200. while (nread == sizeof(buf)); // has next unread
  201. int pid_status;
  202. pid_t result = waitpid(mproc->pid, &pid_status, WNOHANG);
  203. if (result == -1) {
  204. exit_reason = "err_waitpid";
  205. break;
  206. }
  207. if (result != 0) {
  208. if (WIFEXITED(pid_status)) {
  209. exit_reason = "normal";
  210. exit_magic = WEXITSTATUS(pid_status);
  211. }
  212. else if (WIFSIGNALED(pid_status)) {
  213. exit_reason = "sig";
  214. exit_error = WIFSIGNALED(pid_status);
  215. }
  216. else {
  217. exit_reason = "exit_unknown";
  218. }
  219. break;
  220. }
  221. usleep(100);
  222. }
  223. close(mproc->master_pty);
  224. CT_PUSH(tx_command_type_stoped, exit_magic, mproc->tid, exit_error, exit_reason);
  225. mproc->inuse = 0;
  226. return NULL;
  227. }
  228. void *mproc_io_thread_pipe(management_process *mproc) {
  229. posix_spawn_file_actions_t actions;
  230. posix_spawn_file_actions_init(&actions);
  231. posix_spawn_file_actions_addclose(&actions, mproc->pipe_in[1]);
  232. posix_spawn_file_actions_addclose(&actions, mproc->pipe_out[0]);
  233. posix_spawn_file_actions_adddup2(&actions, mproc->pipe_in[0], STDIN_FILENO);
  234. posix_spawn_file_actions_adddup2(&actions, mproc->pipe_out[1], STDOUT_FILENO);
  235. char *const argv[] = {
  236. "/bin/sh", "-c", mproc->command, NULL
  237. };
  238. char *const environment[] = {
  239. NULL
  240. };
  241. int ret = posix_spawnp(&mproc->pid, argv[0], &actions, NULL, argv, environment);
  242. posix_spawn_file_actions_destroy(&actions);
  243. pthread_mutex_unlock(&processes_lock);
  244. if (ret != 0) {
  245. close(mproc->pipe_in[0]);
  246. close(mproc->pipe_in[1]);
  247. close(mproc->pipe_out[0]);
  248. close(mproc->pipe_out[1]);
  249. CT_PUSH(tx_command_type_stoped, ret, mproc->tid, 1, CT_LAUNCH_FAIL);
  250. mproc->inuse = 0;
  251. return NULL;
  252. }
  253. close(mproc->pipe_in[0]);
  254. close(mproc->pipe_out[1]);
  255. mproc->stdin_fd = mproc->pipe_in[1];
  256. mproc->stdout_fd = mproc->pipe_out[0];
  257. int flags = fcntl(mproc->stdout_fd, F_GETFL, 0);
  258. fcntl(mproc->stdout_fd, F_SETFL, flags | O_NONBLOCK);
  259. char buf[VMP_STRBUF_LEN];
  260. ssize_t nread = 0;
  261. const char *exit_reason = "unknown";
  262. int8_t exit_error = 0;
  263. int64_t exit_magic = 0;
  264. while (1) {
  265. do {
  266. bzero(buf, sizeof(buf));
  267. nread = read(mproc->stdout_fd, buf, sizeof(buf));
  268. if (nread > 0) {
  269. tx_command cmd = {
  270. .type = tx_command_type_stdout,
  271. .tid = mproc->tid,
  272. .error = 0,
  273. .magic = 0,
  274. .sstdout_len = nread
  275. };
  276. bzero(&cmd.sstdout[0], VMP_STRBUF_LEN);
  277. memcpy(cmd.sstdout, buf, nread);
  278. tx_push(cmd);
  279. }
  280. }
  281. while (nread == sizeof(buf)); // has next unread
  282. int pid_status;
  283. pid_t result = waitpid(mproc->pid, &pid_status, WNOHANG);
  284. if (result == -1) {
  285. exit_reason = "err_waitpid";
  286. break;
  287. }
  288. if (result != 0) {
  289. if (WIFEXITED(pid_status)) {
  290. exit_reason = "normal";
  291. exit_magic = WEXITSTATUS(pid_status);
  292. }
  293. else if (WIFSIGNALED(pid_status)) {
  294. exit_reason = "sig";
  295. exit_error = WIFSIGNALED(pid_status);
  296. }
  297. else {
  298. exit_reason = "exit_unknown";
  299. }
  300. break;
  301. }
  302. usleep(100);
  303. }
  304. close(mproc->stdin_fd);
  305. close(mproc->stdout_fd);
  306. CT_PUSH(tx_command_type_stoped, exit_magic, mproc->tid, exit_error, exit_reason);
  307. mproc->inuse = 0;
  308. }
  309. int8_t find_unused_mproc_nolock() {
  310. for (int8_t tid = 0; tid < VMP_TID_MAX; tid++) {
  311. if (!processes[tid].inuse) {
  312. bzero(&processes[tid], sizeof(management_process));
  313. return tid;
  314. }
  315. }
  316. return -1;
  317. }
  318. void rx_process(rx_command cmd) {
  319. debug_print_rx(cmd);
  320. switch (cmd.type) {
  321. case rx_command_type_runproc: {
  322. pthread_mutex_lock(&processes_lock);
  323. int8_t tid = find_unused_mproc_nolock();
  324. if (tid < 0) {
  325. pthread_mutex_unlock(&processes_lock);
  326. CB_PUSH(cmd.magic, 1, CB_NO_TID);
  327. break;
  328. }
  329. management_process mproc;
  330. bzero(&mproc, sizeof(management_process));
  331. memcpy(&mproc.command[0], cmd.sstdin, sizeof(mproc.command));
  332. mproc.tid = tid;
  333. mproc.inuse = 1;
  334. processes[tid] = mproc;
  335. mproc.win_size = cmd.win_size;
  336. mproc.use_pipe = 0;
  337. CT_PUSH(tx_command_type_cb, cmd.magic, tid, 0, CB_SUCC);
  338. pthread_create(&mproc.io_thread, NULL, &mproc_io_thread_pty, &processes[tid]);
  339. // processes_lock will unlock in mproc_io_thread
  340. break;
  341. }
  342. case rx_command_type_runpipe: {
  343. pthread_mutex_lock(&processes_lock);
  344. int8_t tid = find_unused_mproc_nolock();
  345. if (tid < 0) {
  346. pthread_mutex_unlock(&processes_lock);
  347. CB_PUSH(cmd.magic, 1, CB_NO_TID);
  348. break;
  349. }
  350. management_process mproc;
  351. bzero(&mproc, sizeof(management_process));
  352. memcpy(&mproc.command[0], cmd.sstdin, sizeof(mproc.command));
  353. if (pipe(mproc.pipe_in) == -1 || pipe(mproc.pipe_out) == -1) {
  354. pthread_mutex_unlock(&processes_lock);
  355. close(mproc.pipe_in[0]);
  356. close(mproc.pipe_in[1]);
  357. close(mproc.pipe_out[0]);
  358. close(mproc.pipe_out[1]);
  359. CB_PUSH(cmd.magic, 1, CB_NO_PIPE);
  360. break;
  361. }
  362. mproc.tid = tid;
  363. mproc.inuse = 1;
  364. processes[tid] = mproc;
  365. mproc.use_pipe = 1;
  366. CT_PUSH(tx_command_type_cb, cmd.magic, tid, 0, CB_SUCC);
  367. pthread_create(&mproc.io_thread, NULL, &mproc_io_thread_pipe, &processes[tid]);
  368. // processes_lock will unlock in mproc_io_thread
  369. break;
  370. }
  371. case rx_command_type_killproc: {
  372. if (cmd.tid < 0 || cmd.tid >= VMP_TID_MAX) {
  373. CB_PUSH(cmd.magic, 1, CB_TID_KILLED);
  374. break;
  375. }
  376. pthread_mutex_lock(&processes_lock);
  377. management_process *mproc = &processes[cmd.tid];
  378. if (!mproc->inuse) {
  379. pthread_mutex_unlock(&processes_lock);
  380. CB_PUSH(cmd.magic, 1, CB_TID_KILLED);
  381. break;
  382. }
  383. kill(mproc->pid, SIGKILL);
  384. pthread_mutex_unlock(&processes_lock);
  385. CB_PUSH(cmd.magic, 0, CB_SUCC);
  386. break;
  387. }
  388. case rx_command_type_lsproc: {
  389. char bytemap[VMP_TID_MAX];
  390. bzero(bytemap, sizeof(bytemap));
  391. pthread_mutex_lock(&processes_lock);
  392. for (int tid = 0; tid < VMP_TID_MAX; tid++) {
  393. bytemap[tid] = processes[tid].inuse;
  394. }
  395. pthread_mutex_unlock(&processes_lock);
  396. tx_command tx = {
  397. .type = tx_command_type_cb,
  398. .tid = 0,
  399. .error = 0,
  400. .magic = cmd.magic,
  401. .sstdout_len = VMP_TID_MAX
  402. };
  403. bzero(&tx.sstdout, VMP_STRBUF_LEN);
  404. memcpy(&tx.sstdout, bytemap, VMP_TID_MAX);
  405. tx_push(tx);
  406. break;
  407. }
  408. case rx_command_type_sstdin: {
  409. if (cmd.tid < 0 || cmd.tid >= VMP_TID_MAX) {
  410. CB_PUSH(cmd.magic, 1, CB_TID_KILLED);
  411. break;
  412. }
  413. pthread_mutex_lock(&processes_lock);
  414. management_process *mproc = &processes[cmd.tid];
  415. if (!mproc->inuse) {
  416. pthread_mutex_unlock(&processes_lock);
  417. CB_PUSH(cmd.magic, 1, CB_TID_KILLED);
  418. break;
  419. }
  420. write(mproc->stdin_fd, cmd.sstdin, cmd.sstdin_len);
  421. pthread_mutex_unlock(&processes_lock);
  422. CB_PUSH(cmd.magic, 0, CB_SUCC);
  423. break;
  424. }
  425. case rx_command_type_winsize: {
  426. if (cmd.tid < 0 || cmd.tid >= VMP_TID_MAX) {
  427. CB_PUSH(cmd.magic, 1, CB_TID_KILLED);
  428. break;
  429. }
  430. pthread_mutex_lock(&processes_lock);
  431. management_process *mproc = &processes[cmd.tid];
  432. if (!mproc->inuse) {
  433. pthread_mutex_unlock(&processes_lock);
  434. CB_PUSH(cmd.magic, 1, CB_TID_KILLED);
  435. break;
  436. }
  437. if (!mproc->use_pipe) {
  438. ioctl(mproc->stdin_fd, TIOCGWINSZ, &cmd.win_size);
  439. }
  440. pthread_mutex_unlock(&processes_lock);
  441. CB_PUSH(cmd.magic, 0, CB_SUCC);
  442. break;
  443. }
  444. case rx_command_type_ping: {
  445. CB_PUSH(cmd.magic, 0, "PONG");
  446. break;
  447. }
  448. }
  449. }
  450. int main() {
  451. printf("Boot Success\n");
  452. printf("Welcome to XCVMKit-OS!\n");
  453. RX = open(VMP_DEVICE_RX, 1101824);
  454. TX = open(VMP_DEVICE_TX, 1101825);
  455. if (RX < 0) {
  456. printf("XCVMKit-OS: unable to connect with host.\n");
  457. return -1;
  458. }
  459. if (TX < 0) {
  460. printf("XCVMKit-OS: unable to connect with host.\n");
  461. return -1;
  462. }
  463. pthread_mutex_init(&tx_lock, NULL);
  464. pthread_mutex_init(&processes_lock, NULL);
  465. pthread_t tx_thr;
  466. pthread_create(&tx_thr, NULL, tx_thread, NULL);
  467. tx_command initial_cmd = {
  468. .type = tx_command_type_online,
  469. .tid = 0,
  470. .magic = 0,
  471. .sstdout_len = 0
  472. };
  473. bzero(&initial_cmd.sstdout, VMP_STRBUF_LEN);
  474. tx_push(initial_cmd);
  475. while (1) {
  476. rx_command command;
  477. size_t read_size = read(RX, &command, sizeof(rx_command));
  478. if (read_size == sizeof(rx_command)) {
  479. rx_process(command);
  480. }
  481. usleep(100);
  482. }
  483. return 0;
  484. }