fix permissions to allow users to execute install-wmiirc-lua script
[wmiirc-lua.git] / luaeventloop / lel_instance.c
blob4d1de05ca36e17234a493d774a50199859793f23
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, pid);
186 return 1;
189 /* ------------------------------------------------------------------------
190 * kills off a previously spawned off process and cleans up
191 * (actually, it just closes the fifo)
193 * lua: el:kill_exec(fd)
195 * fd - return from add_exec()
198 int l_eventloop_kill_exec (lua_State *L)
200 struct lel_eventloop *el;
201 int fd;
203 el = lel_checkeventloop (L, 1);
204 fd = luaL_checknumber (L, 2);
206 DBGF("** eventloop:kill_exec (%d) **\n", fd);
208 kill_exec (L, el, fd);
210 return 0;
213 static void kill_exec (lua_State *L, struct lel_eventloop *el, int fd)
215 struct lel_program *prog;
216 int status;
218 prog = progs_remove (el, fd);
219 if (! prog)
220 return;
222 if (el->max_fd == prog->fd) {
223 if (el->progs_count)
224 // last entry is the new max
225 el->max_fd = el->progs[el->progs_count-1]->fd;
226 else
227 // there are no more entries
228 el->max_fd = 0;
231 FD_CLR (prog->fd, &el->all_fds);
233 kill (prog->pid, SIGTERM);
234 close (prog->fd);
235 free (prog);
237 // catchup on programs that quit
238 while (waitpid (-1, &status, WNOHANG) > 0);
240 // and we still have to remove it from the table
241 luaL_getmetatable (L, L_EVENTLOOP_MT); // [-3] = get the table
242 lua_pushinteger (L, prog->fd); // [-2] = the key
243 lua_pushnil (L); // [-1] = nil
244 lua_settable (L, -3); // eventloop[fd] = function
246 // cleanup
247 lua_gc (L, LUA_GCSTEP, 10);
250 /* ------------------------------------------------------------------------
251 * runs the select loop over all registered execs with timeout
253 * lua: el.run_loop (timeout)
255 int l_eventloop_run_loop (lua_State *L)
257 struct lel_eventloop *el;
258 int timeout;
259 fd_set rfds, xfds;
260 struct timeval tv;
262 el = lel_checkeventloop (L, 1);
263 timeout = luaL_optnumber (L, 2, 0);
265 DBGF("** eventloop:run_loop (%d) **\n", timeout);
267 // init for timeout
268 tv.tv_sec = timeout;
269 tv.tv_usec = 0;
271 // run the loop
272 for (;;) {
273 int i, status, rc;
275 // catchup on programs that quit
276 while (waitpid (-1, &status, WNOHANG) > 0);
278 // init for select
279 rfds = el->all_fds;
281 // wait for the next event
282 rc = select (el->max_fd+1, &rfds, NULL, &xfds, &tv);
283 if (rc<0)
284 return lel_pusherror (L, "select failed");
286 if (!rc)
287 // timeout
288 break;
290 for (i=(el->progs_count-1); i>=0; i--) {
291 struct lel_program *prog;
292 bool dead = false;
294 prog = el->progs[i];
296 if (FD_ISSET (prog->fd, &rfds)) {
297 rc = loop_handle_event (L, prog);
298 if (rc<=0)
299 dead = true;
301 // count could have changed in callback
302 if (i >= el->progs_count)
303 break;
306 if (dead || FD_ISSET (prog->fd, &xfds)) {
307 DBGF("** killing %d (fd=%d) **\n",
308 prog->pid, prog->fd);
309 kill_exec(L, el, prog->fd);
314 return 0;
317 /* ------------------------------------------------------------------------
318 * terminates all executables
320 int l_eventloop_kill_all (lua_State *L)
322 struct lel_eventloop *el;
323 int i;
325 el = lel_checkeventloop (L, 1);
327 for (i=(el->progs_count-1); i>=0; i--) {
328 struct lel_program *prog;
330 prog = el->progs[i];
331 kill_exec (L, el, prog->fd);
334 return 0;
337 /* ------------------------------------------------------------------------
338 * read more data and call callbacks
341 static void prog_read_more (lua_State *L, struct lel_program *prog)
343 int rc;
344 char *buf;
345 ssize_t len;
347 if (!prog->buf_len) {
348 // reset pos to beginning
349 prog->buf_pos = 0;
352 buf = prog->buf + prog->buf_pos;
353 len = LEL_PROGRAM_IO_BUF_SIZE - prog->buf_pos - prog->buf_len;
355 if (len<=0) {
356 // reached the end
357 if (! prog->buf_pos) {
358 // cannot shift data down
359 prog->read_rc = -1;
360 prog->read_errno = EBUSY;
361 return;
364 // shift data down to make some more room
365 memmove (prog->buf, buf, prog->buf_len);
366 prog->buf_pos = 0;
367 buf = prog->buf;
370 // get some data
371 rc = read (prog->fd, buf, len);
373 prog->read_rc = rc;
374 prog->read_errno = errno;
376 if (rc>0) {
377 prog->buf_len = rc;
378 buf[rc] = 0;
383 static void lua_issue_callback (lua_State *L, struct lel_program *prog,
384 char *string)
386 int top;
388 // backup top of stack
389 top = lua_gettop (L);
391 // find the call back function
392 luaL_getmetatable (L, L_EVENTLOOP_MT); // [-2] = get the table
393 lua_pushinteger (L, prog->fd); // [-1] = the key
394 lua_gettable (L, -2); // push (eventloop[fd])
396 if (string) {
397 // success
398 lua_pushstring (L, string);
399 lua_call (L, 1, 0);
401 } else if (prog->read_rc == 0) {
402 // stream ended
403 lua_pushnil (L);
404 lua_pushstring (L, "EOF");
405 lua_call (L, 2, 0);
407 } else if (prog->read_rc < 0) {
408 // error reading
409 lua_pushnil (L);
410 lua_pushstring (L, strerror(prog->read_errno));
411 lua_call (L, 2, 0);
414 // restore top of stack
415 lua_settop (L, top);
418 static int loop_handle_event (lua_State *L, struct lel_program *prog)
420 char *s, *e, *cr;
422 prog_read_more (L, prog);
424 while (prog->buf_len) {
425 // as long as we have some data we try to find a full line
426 s = prog->buf + prog->buf_pos;
427 e = s + prog->buf_len;
429 cr = strchr (s, '\n');
430 if (cr) {
431 // we have a match: s..cr is our substring
432 int len = (cr-s) + 1;
433 *cr = 0;
435 lua_issue_callback (L, prog, s);
437 prog->buf_pos += len;
438 prog->buf_len -= len;
440 } else if (!prog->buf_pos) {
441 // no match and we cannot even read more out of
442 // the buffer; we have to return the partial buffer
443 lua_issue_callback (L, prog, s);
445 prog->buf_len = 0;
447 } else {
448 // no match, we will try to read more on next select()
449 // read event
450 break;
454 return prog->read_rc;