source: popexec.c @ afbf668

barnowl_perlaimdebianowlrelease-1.10release-1.4release-1.5release-1.6release-1.7release-1.8release-1.9
Last change on this file since afbf668 was afbf668, checked in by Erik Nygren <nygren@mit.edu>, 20 years ago
pexec will now incrimentally display data as it is output by the child process. Additionally, commands running under pexec may now be killed by quitting out of the popless window. Added muxevents select loop dispatcher. File descriptors may be registered with muxevents and handlers will be dispatched to when data is available for non-blocking read/write/except. Switched the stderr_redir stuff to use muxevents.
  • 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
10static const char fileIdent[] = "$Id$";
11
12/* starts up popexec in a new viewwin */
13owl_popexec *owl_popexec_new(char *command)
14{
15  owl_popexec *pe;
16  owl_popwin *pw;
17  owl_viewwin *v;
18  int pipefds[2], child_write_fd, parent_read_fd;
19  int pid;
20
21  pe = owl_malloc(sizeof(owl_popexec));
22  if (!pe) return NULL;
23  pe->winactive=0;
24  pe->pid=0;
25  pe->refcount=0;
26
27  pw=owl_global_get_popwin(&g);
28  pe->vwin=v=owl_global_get_viewwin(&g);
29
30  owl_popwin_up(pw);
31  owl_viewwin_init_text(v, owl_popwin_get_curswin(pw),
32                        owl_popwin_get_lines(pw), owl_popwin_get_cols(pw),
33                        "");
34  owl_popwin_refresh(pw);
35  owl_viewwin_redisplay(v, 0);
36  owl_global_set_needrefresh(&g);
37  owl_viewwin_set_onclose_hook(v, owl_popexec_viewwin_onclose, pe);
38  pe->refcount++;
39
40  if (0 != pipe(pipefds)) {
41    owl_function_error("owl_function_popless_exec: pipe failed\n");
42    return NULL;
43  }
44  parent_read_fd = pipefds[0];
45  child_write_fd = pipefds[1];
46  pid = fork();
47  if (pid == -1) {
48    close(pipefds[0]);
49    close(pipefds[1]);
50    owl_function_error("owl_function_popless_exec: fork failed\n");
51    return NULL;
52  } else if (pid != 0) {
53    /* still in owl */
54    int muxhandle;
55    pe->pid=pid;
56    pe->winactive=1;
57    pe->rfd = parent_read_fd;
58    close(child_write_fd);
59    muxhandle = owl_muxevents_add(owl_global_get_muxevents(&g),
60                                  parent_read_fd, OWL_MUX_READ|OWL_MUX_EXCEPT,
61                                  owl_popexec_inputhandler, (void*)pe);
62    pe->refcount++;
63  } else {
64    /* in the child process */
65    char *argv[4];
66    int i;
67    int fdlimit = sysconf(_SC_OPEN_MAX);
68
69    for (i=0; i<fdlimit; i++) {
70      if (i!=child_write_fd) close(i);
71    }
72    dup2(child_write_fd, 1 /*stdout*/);
73    dup2(child_write_fd, 2 /*stderr*/);
74    close(child_write_fd);
75   
76    while(0) {
77      write(child_write_fd, "meep\n", 5);
78      sleep(1);
79    }
80
81    argv[0] = "sh";
82    argv[1] = "-c";
83    argv[2] = command;
84    argv[3] = 0;
85    execv("/bin/sh", argv);
86    _exit(127);
87  }
88
89  return pe;
90}
91
92void owl_popexec_inputhandler(int handle, int fd, int eventmask, void *data) {
93  owl_popexec *pe = (owl_popexec*)data;
94  int navail, bread, rv_navail;
95  char *buf;
96  int status;
97
98  if (!pe) return;
99
100  /* If pe->winactive is 0 then the vwin has closed.
101   * If pe->pid is 0 then the child has already been reaped.
102   * if pe->rfd is -1 then the fd has been closed out.
103   * Under these cases we want to get to a state where:
104   *   - data read until end if child running
105   *   - child reaped
106   *   - fd closed
107   *   - callback removed
108   */
109
110  /* the viewwin has closed */
111  if (pe->rfd<0 && !pe->pid && !pe->winactive) {
112    owl_muxevents_remove(owl_global_get_muxevents(&g), handle);
113    owl_popexec_unref(pe);
114    return;
115  }
116
117  if (0 != (rv_navail = ioctl(fd, FIONREAD, (void*)&navail))) {
118    owl_function_debugmsg("ioctl error");
119  }
120
121  /* check to see if the child has ended gracefully and no more data is
122   * ready to be read... */
123  if (navail==0 && pe->pid>0 && waitpid(pe->pid, &status, WNOHANG) > 0) {
124    owl_function_debugmsg("waitpid got child status: <%d>\n", status);
125    pe->pid = 0;
126    if (pe->winactive) { 
127      owl_viewwin_append_text(pe->vwin, "\n");
128      owl_viewwin_redisplay(pe->vwin, 1);
129    }
130    if (pe->rfd>0) {
131      close(pe->rfd);
132      pe->rfd = -1;
133    }
134    owl_muxevents_remove(owl_global_get_muxevents(&g), handle);
135    owl_popexec_unref(pe);
136    return;
137  }
138
139  if (pe->rfd<0 || !pe->pid || !pe->winactive || rv_navail) {
140    owl_function_error("popexec should not have reached this point");
141    return;
142  }
143
144  if (navail<=0) return;
145  if (navail>1024) { navail = 1024; }
146  buf = owl_malloc(navail+1);
147  owl_function_debugmsg("about to read %d\n", navail);
148  bread = read(fd, buf, navail);
149  if (bread<0) {
150    perror("read");
151    owl_function_debugmsg("read error");
152  }
153  if (buf[navail-1] != '\0') {
154    buf[navail] = '\0';
155  }
156  owl_function_debugmsg("got data:  <%s>\n", buf);
157  if (pe->winactive) {
158    owl_viewwin_append_text(pe->vwin, buf);
159    owl_viewwin_redisplay(pe->vwin, 1);
160  }
161  owl_free(buf);
162 
163}
164
165void owl_popexec_viewwin_onclose(owl_viewwin *vwin, void *data) {
166  owl_popexec *pe = (owl_popexec*)data;
167  int status, rv;
168
169  pe->winactive = 0;
170  if (pe->rfd>0) {
171    close(pe->rfd);
172    pe->rfd = -1;
173  }
174  if (pe->pid) {
175    /* TODO: we should handle the case where SIGTERM isn't good enough */
176    rv = kill(pe->pid, SIGTERM);
177    owl_function_debugmsg("kill of pid %d returned %d", pe->pid, rv);
178    rv = waitpid(pe->pid, &status, 0);
179    owl_function_debugmsg("waidpid returned %d, status %d", rv, status);
180    pe->pid = 0;
181  }
182  owl_popexec_unref(pe);
183}
184
185void owl_popexec_unref(owl_popexec *pe) {
186  pe->refcount--;
187  if (pe->refcount<=0) {
188    owl_free(pe);
189  }
190}
Note: See TracBrowser for help on using the repository browser.