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