source: popexec.c @ 3e55268

release-1.10release-1.6release-1.7release-1.8release-1.9
Last change on this file since 3e55268 was 2a17b63, checked in by Nelson Elhage <nelhage@mit.edu>, 15 years ago
Push and pop contexts whenever we change context. This allows us to get rid of the awful hack in owl_process_input_char, and opens the path to being able to invoke popup windows from the editwin and vice versa (In fact, you can now do so without segfaulting, although the display layer does not yet quite do the right thing).
  • Property mode set to 100644
File size: 4.7 KB
Line 
1#include "owl.h"
2#ifdef HAVE_SYS_IOCTL_H
3#include <sys/ioctl.h>
4#endif
5#ifdef HAVE_SYS_FILIO_H
6#include <sys/filio.h>
7#endif
8#include <sys/wait.h>
9
10/* starts up popexec in a new viewwin */
11owl_popexec *owl_popexec_new(const char *command)
12{
13  owl_popexec *pe;
14  owl_popwin *pw;
15  owl_viewwin *v;
16  int pipefds[2], child_write_fd, parent_read_fd;
17  pid_t pid;
18
19  pe = owl_malloc(sizeof(owl_popexec));
20  if (!pe) return NULL;
21  pe->winactive=0;
22  pe->pid=0;
23  pe->refcount=0;
24
25  pw=owl_global_get_popwin(&g);
26  pe->vwin=v=owl_global_get_viewwin(&g);
27
28  owl_popwin_up(pw);
29  owl_global_push_context(&g, OWL_CTX_POPLESS, v, "popless");
30  owl_viewwin_init_text(v, owl_popwin_get_curswin(pw),
31                        owl_popwin_get_lines(pw), owl_popwin_get_cols(pw),
32                        "");
33  owl_popwin_refresh(pw);
34  owl_viewwin_redisplay(v, 0);
35  owl_global_set_needrefresh(&g);
36  owl_viewwin_set_onclose_hook(v, owl_popexec_viewwin_onclose, pe);
37  pe->refcount++;
38
39  if (0 != pipe(pipefds)) {
40    owl_function_error("owl_function_popless_exec: pipe failed\n");
41    return NULL;
42  }
43  parent_read_fd = pipefds[0];
44  child_write_fd = pipefds[1];
45  pid = fork();
46  if (pid == -1) {
47    close(pipefds[0]);
48    close(pipefds[1]);
49    owl_function_error("owl_function_popless_exec: fork failed\n");
50    return NULL;
51  } else if (pid != 0) {
52    close(child_write_fd);
53    /* still in owl */
54    pe->pid=pid;
55    pe->winactive=1;
56    pe->dispatch = owl_select_add_io_dispatch(parent_read_fd, OWL_IO_READ|OWL_IO_EXCEPT, &owl_popexec_inputhandler, &owl_popexec_delete_dispatch, pe);
57    pe->refcount++;
58  } else {
59    /* in the child process */
60    int i;
61    int fdlimit = sysconf(_SC_OPEN_MAX);
62
63    for (i=0; i<fdlimit; i++) {
64      if (i!=child_write_fd) close(i);
65    }
66    dup2(child_write_fd, 1 /*stdout*/);
67    dup2(child_write_fd, 2 /*stderr*/);
68    close(child_write_fd);
69
70    execl("/bin/sh", "sh", "-c", command, (const char *)NULL);
71    _exit(127);
72  }
73
74  return pe;
75}
76
77void owl_popexec_inputhandler(const owl_io_dispatch *d, void *data)
78{
79  owl_popexec *pe = data;
80  int navail, bread, rv_navail;
81  char *buf;
82  int status;
83
84  if (!pe) return;
85
86  /* If pe->winactive is 0 then the vwin has closed.
87   * If pe->pid is 0 then the child has already been reaped.
88   * if d->fd is -1 then the fd has been closed out.
89   * Under these cases we want to get to a state where:
90   *   - data read until end if child running
91   *   - child reaped
92   *   - fd closed
93   *   - callback removed
94   */
95
96  /* the viewwin has closed */
97  if (!pe->pid && !pe->winactive) {
98    owl_select_remove_io_dispatch(d);
99    return;
100  }
101
102  if (0 != (rv_navail = ioctl(d->fd, FIONREAD, &navail))) {
103    owl_function_debugmsg("ioctl error");
104  }
105
106  /* check to see if the child has ended gracefully and no more data is
107   * ready to be read... */
108  if (navail==0 && pe->pid>0 && waitpid(pe->pid, &status, WNOHANG) > 0) {
109    owl_function_debugmsg("waitpid got child status: <%d>\n", status);
110    pe->pid = 0;
111    if (pe->winactive) { 
112      owl_viewwin_append_text(pe->vwin, "\n");
113      owl_viewwin_redisplay(pe->vwin, 1);
114    }
115    owl_select_remove_io_dispatch(d);
116    return;
117  }
118
119  if (d->fd<0 || !pe->pid || !pe->winactive || rv_navail) {
120    owl_function_error("popexec should not have reached this point");
121    return;
122  }
123
124  if (navail<=0) return;
125  if (navail>1024) { navail = 1024; }
126  buf = owl_malloc(navail+1);
127  owl_function_debugmsg("about to read %d", navail);
128  bread = read(d->fd, buf, navail);
129  if (bread<0) {
130    perror("read");
131    owl_function_debugmsg("read error");
132  }
133  if (buf[navail-1] != '\0') {
134    buf[navail] = '\0';
135  }
136  owl_function_debugmsg("got data:  <%s>", buf);
137  if (pe->winactive) {
138    owl_viewwin_append_text(pe->vwin, buf);
139    owl_viewwin_redisplay(pe->vwin, 1);
140  }
141  owl_free(buf);
142 
143}
144
145void owl_popexec_delete_dispatch(const owl_io_dispatch *d)
146{
147  owl_popexec *pe = d->data;
148  close(d->fd);
149  owl_popexec_unref(pe);
150}
151
152void owl_popexec_viewwin_onclose(owl_viewwin *vwin, void *data)
153{
154  owl_popexec *pe = data;
155  int status, rv;
156
157  pe->winactive = 0;
158  if (pe->dispatch->fd > 0) {
159    owl_select_remove_io_dispatch(pe->dispatch);
160  }
161  if (pe->pid) {
162    /* TODO: we should handle the case where SIGTERM isn't good enough */
163    rv = kill(pe->pid, SIGTERM);
164    owl_function_debugmsg("kill of pid %d returned %d", pe->pid, rv);
165    rv = waitpid(pe->pid, &status, 0);
166    owl_function_debugmsg("waidpid returned %d, status %d", rv, status);
167    pe->pid = 0;
168  }
169  owl_function_debugmsg("unref of %p from onclose", pe);
170  owl_popexec_unref(pe);
171}
172
173void owl_popexec_unref(owl_popexec *pe)
174{
175  owl_function_debugmsg("unref of %p was %d", pe, pe->refcount);
176  pe->refcount--;
177  if (pe->refcount<=0) {
178    owl_function_debugmsg("doing free of %p", pe);
179    owl_free(pe);
180  }
181}
Note: See TracBrowser for help on using the repository browser.