fix client_focused()
[wmiirc-lua.git] / luaeventloop / lel_instance.c
blob6db59a9a0755e259067e38f4f7790ad809e2dc5c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <stdint.h>
4 #include <stdbool.h>
5 #include <unistd.h>
6 #include <string.h>
7 #include <errno.h>
8 #include <time.h>
9 #include <signal.h>
10 #include <ctype.h>
11 #include <sys/types.h>
12 #include <sys/wait.h>
14 #include <lua.h>
15 #include <lauxlib.h>
17 #include "lel_debug.h"
18 #include "lel_util.h"
19 #include "lel_instance.h"
21 // local hepers
22 static int loop_handle_event (lua_State *L, struct lel_program *prog);
23 static void kill_exec (lua_State *L, struct lel_eventloop *el, int fd);
25 /* ------------------------------------------------------------------------
26 * utility functions
29 struct lel_eventloop *lel_checkeventloop (lua_State *L, int narg)
31 void *ud = luaL_checkudata (L, narg, L_EVENTLOOP_MT);
32 luaL_argcheck (L, ud != NULL, 1, "`eventloop' expected");
33 return (struct lel_eventloop*)ud;
36 int l_eventloop_tostring (lua_State *L)
38 struct lel_eventloop *el = lel_checkeventloop (L, 1);
39 lua_pushfstring (L, "eventloop instance %p", el);
40 return 1;
43 /* ------------------------------------------------------------------------
44 * dealing with program list
47 static int progs_compare (const void *_one, const void *_two)
49 const struct lel_program *const*one = _one;
50 const struct lel_program *const*two = _two;
52 return (*one)->fd - (*two)->fd;
55 static void progs_add (struct lel_eventloop *el, struct lel_program *prog)
57 // make more room
58 if (el->progs_size <= el->progs_count) {
59 size_t bytes;
61 el->progs_size += LEL_PROGS_ARRAY_GROWS_BY;
62 bytes = el->progs_size * sizeof(struct lel_program*);
64 el->progs = realloc (el->progs, bytes);
65 if (!el->progs) {
66 // TODO: we could handle this better then blowing away
67 // all of our data if we run out of room. For now exit.
68 perror ("malloc");
69 exit (1);
73 el->progs[el->progs_count++] = prog;
75 qsort (el->progs, el->progs_count, sizeof (struct lel_program*),
76 progs_compare);
79 static struct lel_program * progs_remove (struct lel_eventloop *el, int fd)
81 struct lel_program key = {.fd = fd};
82 struct lel_program *pkey = &key;
83 struct lel_program *found;
84 struct lel_program **pfound;
85 size_t end_bytes;
87 // find the program
88 pfound = bsearch (&pkey, el->progs, el->progs_count,
89 sizeof (struct lel_program*), progs_compare);
90 if (!pfound)
91 return NULL;
93 found = *pfound;
94 if (!found)
95 return NULL;
97 // adjust remaining entries
98 el->progs_count --;
99 end_bytes = (char*)(el->progs + el->progs_count) - (char*)pfound;
100 memmove (pfound, pfound+1, end_bytes);
102 return found;
106 /* ------------------------------------------------------------------------
107 * executes a new process to handle events from another source
109 * lua: fd = el:add_exec(cmd, function)
111 * cmd - a string with program and parameters for execution
112 * function - a function to call back with data read
113 * fd - returned is the file descriptor or nil on error
116 int l_eventloop_add_exec (lua_State *L)
118 struct lel_eventloop *el;
119 struct lel_program *prog;
120 const char *cmd;
121 int pfds[2]; // 0 is server, 1 is client
122 int rc, pid;
124 el = lel_checkeventloop (L, 1);
125 cmd = luaL_checkstring (L, 2);
126 (void)luaL_checktype (L, 3, LUA_TFUNCTION);
128 DBGF("** eventloop:add_exec (%s, ...) **\n", cmd);
130 // create a new program entry
131 prog = (struct lel_program*) malloc (sizeof (struct lel_program)
132 // we allocate the buffer and room for a terminator at the end
133 + LEL_PROGRAM_IO_BUF_SIZE + 1);
134 if (!prog)
135 return lel_pusherror (L, "failed to allocate program structure");
137 // spawn off a worker process
138 rc = pipe(pfds);
139 if (rc<0) {
140 free (prog);
141 return lel_pusherror (L, "failed to create a pipe");
144 pid = vfork();
145 if (pid<0) { // fork failed...
146 free (prog);
147 close (pfds[0]);
148 close (pfds[1]);
149 return lel_pusherror (L, "failed to fork()");
152 if (! pid) { // client...
153 close (pfds[0]); // close the server end
154 dup2(pfds[1], 1); // stdout to client's end of pipe
155 close (pfds[1]); // close the client end
156 execlp ("sh", "sh", "-c", cmd, NULL);
157 exit (1);
160 // back in server...
161 close (pfds[1]); // close the client end
163 // time to setup the program entry
164 memset (prog, 0, sizeof(*prog));
166 prog->cmd = strdup (cmd);
167 prog->pid = pid;
168 prog->fd = pfds[0];
170 if (el->max_fd < prog->fd)
171 el->max_fd = prog->fd;
173 FD_SET (prog->fd, &el->all_fds);
175 // add to the list
176 progs_add (el, prog);
178 // everything is setup, but we need to get a hold of the function later;
179 // we add the function to the L_EVENTLOOP_MT with the fd as the key...
180 luaL_getmetatable (L, L_EVENTLOOP_MT); // [-3] = get the table
181 lua_pushinteger (L, prog->fd); // [-2] = the key
182 lua_pushvalue (L, 3); // [-1] = the function (3rd arg)
183 lua_settable (L, -3); // eventloop[fd] = function
185 lua_pushinteger (L, prog->fd);
186 return 1;
189 /* ------------------------------------------------------------------------
190 * checks if an executable is still running
192 * lua: running = el:check_exec(fd)
194 * fd - file descriptor returned from el:add_exec()
195 * running - boolean indicating if it's still running
198 int l_eventloop_check_exec (lua_State *L)
200 struct lel_eventloop *el;
201 int fd, i;
202 bool found = false;
204 el = lel_checkeventloop(L, 1);
205 fd = luaL_checknumber(L, 2);
207 DBGF("** eventloop:check_exec (%d) **\n", fd);
209 for (i=(el->progs_count-1); i>=0; i--) {
210 struct lel_program *prog;
212 prog = el->progs[i];
214 if (prog->fd != fd)
215 continue;
217 found = true;
218 break;
221 lua_pushboolean(L, found);
222 return 1;
226 /* ------------------------------------------------------------------------
227 * kills off a previously spawned off process and cleans up
228 * (actually, it just closes the fifo)
230 * lua: el:kill_exec(fd)
232 * fd - return from add_exec()
235 int l_eventloop_kill_exec (lua_State *L)
237 struct lel_eventloop *el;
238 int fd;
240 el = lel_checkeventloop (L, 1);
241 fd = luaL_checknumber (L, 2);
243 DBGF("** eventloop:kill_exec (%d) **\n", fd);
245 kill_exec (L, el, fd);
247 return 0;
250 static void kill_exec (lua_State *L, struct lel_eventloop *el, int fd)
252 struct lel_program *prog;
253 int status;
255 prog = progs_remove (el, fd);
256 if (! prog)
257 return;
259 if (el->max_fd == prog->fd) {
260 if (el->progs_count)
261 // last entry is the new max
262 el->max_fd = el->progs[el->progs_count-1]->fd;
263 else
264 // there are no more entries
265 el->max_fd = 0;
268 FD_CLR (prog->fd, &el->all_fds);
270 kill (prog->pid, SIGTERM);
271 close (prog->fd);
272 free (prog);
274 // catchup on programs that quit
275 while (waitpid (-1, &status, WNOHANG) > 0);
277 // and we still have to remove it from the table
278 luaL_getmetatable (L, L_EVENTLOOP_MT); // [-3] = get the table
279 lua_pushinteger (L, prog->fd); // [-2] = the key
280 lua_pushnil (L); // [-1] = nil
281 lua_settable (L, -3); // eventloop[fd] = nil
283 // cleanup
284 lua_gc (L, LUA_GCSTEP, 10);
287 /* ------------------------------------------------------------------------
288 * runs the select loop over all registered execs with timeout
290 * lua: el.run_loop (timeout)
292 int l_eventloop_run_loop (lua_State *L)
294 struct lel_eventloop *el;
295 int timeout, status;
296 fd_set rfds, xfds;
297 struct timeval tv;
299 el = lel_checkeventloop (L, 1);
300 timeout = luaL_optnumber (L, 2, 0);
302 DBGF("** eventloop:run_loop (%d) **\n", timeout);
304 // init for timeout
305 tv.tv_sec = timeout;
306 tv.tv_usec = 0;
308 // run the loop
309 while (el->progs_count) {
310 int i, rc;
312 // catchup on programs that quit
313 while (waitpid (-1, &status, WNOHANG) > 0);
315 // init for select
316 rfds = el->all_fds;
317 xfds = el->all_fds;
319 // wait for the next event
320 rc = select (el->max_fd+1, &rfds, NULL, &xfds, &tv);
321 if (rc<0)
322 return lel_pusherror (L, "select failed");
324 if (!rc)
325 // timeout
326 break;
328 for (i=(el->progs_count-1); i>=0; i--) {
329 struct lel_program *prog;
330 bool dead = false;
332 prog = el->progs[i];
334 if (FD_ISSET (prog->fd, &rfds)) {
335 rc = loop_handle_event (L, prog);
336 if (rc<=0)
337 dead = true;
339 // count could have changed in callback
340 if (i >= el->progs_count)
341 break;
344 if (dead /* || FD_ISSET (prog->fd, &xfds) */ ) {
345 DBGF("** killing %d (fd=%d) **\n",
346 prog->pid, prog->fd);
347 kill_exec(L, el, prog->fd);
352 // catchup on programs that quit
353 while (waitpid (-1, &status, WNOHANG) > 0);
355 return 0;
358 /* ------------------------------------------------------------------------
359 * terminates all executables
361 int l_eventloop_kill_all (lua_State *L)
363 struct lel_eventloop *el;
364 int i;
366 el = lel_checkeventloop (L, 1);
368 for (i=(el->progs_count-1); i>=0; i--) {
369 struct lel_program *prog;
371 prog = el->progs[i];
372 kill_exec (L, el, prog->fd);
375 return 0;
378 /* ------------------------------------------------------------------------
379 * read more data and call callbacks
382 static void prog_read_more (lua_State *L, struct lel_program *prog)
384 int rc;
385 char *buf;
386 ssize_t len;
388 if (!prog->buf_len) {
389 // reset pos to beginning
390 prog->buf_pos = 0;
393 buf = prog->buf + prog->buf_pos;
394 len = LEL_PROGRAM_IO_BUF_SIZE - prog->buf_pos - prog->buf_len;
396 if (len<=0) {
397 // reached the end
398 if (! prog->buf_pos) {
399 // cannot shift data down
400 prog->read_rc = -1;
401 prog->read_errno = EBUSY;
402 return;
405 // shift data down to make some more room
406 memmove (prog->buf, buf, prog->buf_len);
407 prog->buf_pos = 0;
408 buf = prog->buf;
411 // get some data
412 rc = read (prog->fd, buf, len);
414 prog->read_rc = rc;
415 prog->read_errno = errno;
417 if (rc>0) {
418 prog->buf_len = rc;
419 buf[rc] = 0;
424 static void lua_issue_callback (lua_State *L, struct lel_program *prog,
425 char *string)
427 int top;
429 // backup top of stack
430 top = lua_gettop (L);
432 // find the call back function
433 luaL_getmetatable (L, L_EVENTLOOP_MT); // [-2] = get the table
434 lua_pushinteger (L, prog->fd); // [-1] = the key
435 lua_gettable (L, -2); // push (eventloop[fd])
437 if (string) {
438 // success
439 lua_pushstring (L, string);
440 lua_call (L, 1, 0);
442 } else if (prog->read_rc == 0) {
443 // stream ended
444 lua_pushnil (L);
445 lua_pushstring (L, "EOF");
446 lua_call (L, 2, 0);
448 } else if (prog->read_rc < 0) {
449 // error reading
450 lua_pushnil (L);
451 lua_pushstring (L, strerror(prog->read_errno));
452 lua_call (L, 2, 0);
455 // restore top of stack
456 lua_settop (L, top);
459 static int loop_handle_event (lua_State *L, struct lel_program *prog)
461 char *s, *e, *cr;
463 prog_read_more (L, prog);
465 while (prog->buf_len) {
466 // as long as we have some data we try to find a full line
467 s = prog->buf + prog->buf_pos;
468 e = s + prog->buf_len;
470 cr = strchr (s, '\n');
471 if (cr) {
472 // we have a match: s..cr is our substring
473 int len = (cr-s) + 1;
474 *cr = 0;
476 lua_issue_callback (L, prog, s);
478 prog->buf_pos += len;
479 prog->buf_len -= len;
481 } else if (!prog->buf_pos) {
482 // no match and we cannot even read more out of
483 // the buffer; we have to return the partial buffer
484 lua_issue_callback (L, prog, s);
486 prog->buf_len = 0;
488 } else {
489 // no match, we will try to read more on next select()
490 // read event
491 break;
495 return prog->read_rc;