1 #include <stdio.h>
2 #include <stdarg.h>
3 #include <sys/types.h>
4 #include <sys/stat.h>
5 #include <sys/time.h>
6 #include <fcntl.h>
7 #include <signal.h>
8 #include <utlist.h>
9 #include "mle.h"
10
11 // Return a new aproc_t
aproc_new(editor_t * editor,void * owner,aproc_t ** owner_aproc,char * shell_cmd,int rw,aproc_cb_t callback)12 aproc_t *aproc_new(editor_t *editor, void *owner, aproc_t **owner_aproc, char *shell_cmd, int rw, aproc_cb_t callback) {
13 aproc_t *aproc;
14 aproc = calloc(1, sizeof(aproc_t));
15 aproc->editor = editor;
16 aproc_set_owner(aproc, owner, owner_aproc);
17 if (rw) {
18 if (!util_popen2(shell_cmd, 0, NULL, &aproc->rfd, &aproc->wfd, &aproc->pid)) {
19 goto aproc_new_failure;
20 }
21 aproc->rpipe = fdopen(aproc->rfd, "r");
22 aproc->wpipe = fdopen(aproc->wfd, "w");
23 } else {
24 if (!(aproc->rpipe = popen(shell_cmd, "r"))) {
25 goto aproc_new_failure;
26 }
27 aproc->rfd = fileno(aproc->rpipe);
28 }
29 setvbuf(aproc->rpipe, NULL, _IONBF, 0);
30 if (aproc->wpipe) setvbuf(aproc->wpipe, NULL, _IONBF, 0);
31 aproc->callback = callback;
32 DL_APPEND(editor->aprocs, aproc);
33 return aproc;
34
35 aproc_new_failure:
36 free(aproc);
37 return NULL;
38 }
39
40 // Set aproc owner
aproc_set_owner(aproc_t * aproc,void * owner,aproc_t ** owner_aproc)41 int aproc_set_owner(aproc_t *aproc, void *owner, aproc_t **owner_aproc) {
42 if (aproc->owner_aproc) {
43 *aproc->owner_aproc = NULL;
44 }
45 *owner_aproc = aproc;
46 aproc->owner = owner;
47 aproc->owner_aproc = owner_aproc;
48 return MLE_OK;
49 }
50
51 // Destroy an aproc_t
aproc_destroy(aproc_t * aproc,int preempt)52 int aproc_destroy(aproc_t *aproc, int preempt) {
53 DL_DELETE(aproc->editor->aprocs, aproc);
54 if (aproc->owner_aproc) *aproc->owner_aproc = NULL;
55 if (preempt) {
56 if (aproc->rfd) close(aproc->rfd);
57 if (aproc->wfd) close(aproc->wfd);
58 if (aproc->pid) kill(aproc->pid, SIGTERM);
59 }
60 if (aproc->rpipe) pclose(aproc->rpipe);
61 if (aproc->wpipe) pclose(aproc->wpipe);
62 free(aproc);
63 return MLE_OK;
64 }
65
66 // Manage async procs, giving priority to user input. Return 1 if drain should
67 // be called again, else return 0.
aproc_drain_all(aproc_t * aprocs,int * ttyfd)68 int aproc_drain_all(aproc_t *aprocs, int *ttyfd) {
69 int maxfd;
70 fd_set readfds;
71 aproc_t *aproc;
72 aproc_t *aproc_tmp;
73 char buf[1024 + 1];
74 ssize_t nbytes;
75 int rc;
76
77 // Exit early if no aprocs
78 if (!aprocs) return 0;
79
80 // Open ttyfd if not already open
81 if (!*ttyfd) {
82 if ((*ttyfd = open("/dev/tty", O_RDONLY)) < 0) {
83 // TODO error
84 return 0;
85 }
86 }
87
88 // Add tty to readfds
89 FD_ZERO(&readfds);
90 FD_SET(*ttyfd, &readfds);
91
92 // Add async procs to readfds
93 // Simultaneously check for solo, which takes precedence over everything
94 maxfd = *ttyfd;
95 DL_FOREACH(aprocs, aproc) {
96 FD_SET(aproc->rfd, &readfds);
97 if (aproc->rfd > maxfd) maxfd = aproc->rfd;
98 }
99
100 // Perform select
101 rc = select(maxfd + 1, &readfds, NULL, NULL, NULL);
102 if (rc < 0) {
103 return 0; // TODO error
104 } else if (rc == 0) {
105 return 1; // Nothing to read, call again
106 }
107
108 if (FD_ISSET(*ttyfd, &readfds)) {
109 // Immediately give priority to user input
110 return 0;
111 } else {
112 // Read async procs
113 DL_FOREACH_SAFE(aprocs, aproc, aproc_tmp) {
114 // Read and invoke callback
115 if (FD_ISSET(aproc->rfd, &readfds)) {
116 nbytes = read(aproc->rfd, &buf, 1024);
117 buf[nbytes] = '\0';
118 aproc->callback(aproc, buf, nbytes);
119 if (nbytes == 0) aproc->is_done = 1;
120 }
121 // Destroy on eof.
122 // Not sure if ferror and feof have any effect here given we're not
123 // using fread.
124 if (ferror(aproc->rpipe) || feof(aproc->rpipe) || aproc->is_done) {
125 aproc_destroy(aproc, 0);
126 }
127 }
128 }
129
130 return 1;
131 }
132