b4dab19cbf63014c1e1af5bdf6c9cd862d46c821
[profile/ivi/murphy.git] / src / plugins / system-monitor / process-watch.c
1 /*
2  * Copyright (c) 2014, Intel Corporation
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  *  * Redistributions of source code must retain the above copyright notice,
9  *    this list of conditions and the following disclaimer.
10  *  * Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *  * Neither the name of Intel Corporation nor the names of its contributors
14  *    may be used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 #include <errno.h>
31 #include <string.h>
32 #include <pwd.h>
33 #include <grp.h>
34 #include <sys/types.h>
35
36 #include <murphy/common/macros.h>
37 #include <murphy/common/debug.h>
38 #include <murphy/common/log.h>
39 #include <murphy/common/mm.h>
40 #include <murphy/core/lua-utils/object.h>
41 #include <murphy/core/lua-utils/funcbridge.h>
42 #include <murphy/core/lua-bindings/murphy.h>
43
44 #include "process-watch.h"
45
46 /*
47  * process watch object
48  */
49
50 #define PROCESS_WATCH_LUA_CLASS MRP_LUA_CLASS(process_watch, lua)
51 #define RO                      MRP_LUA_CLASS_READONLY
52 #define NOINIT                  MRP_LUA_CLASS_NOINIT
53 #define NOFLAGS                 MRP_LUA_CLASS_NOFLAGS
54 #define setmember               process_watch_setmember
55 #define getmember               process_watch_getmember
56
57 static int process_watch_no_constructor(lua_State *L);
58 static void process_watch_destroy(void *data);
59 static void process_watch_changed(void *data, lua_State *L, int member);
60 static ssize_t process_watch_tostring(mrp_lua_tostr_mode_t mode, char *buf,
61                                       size_t size, lua_State *L, void *data);
62 static int process_watch_setmember(void *data, lua_State *L, int member,
63                                    mrp_lua_value_t *v);
64 static int process_watch_getmember(void *data, lua_State *L, int member,
65                                    mrp_lua_value_t *v);
66 static int process_watch_delete(lua_State *L);
67
68 static int setup_filter(process_watch_lua_t *w, lua_State *L, int filterref);
69
70
71 MRP_LUA_METHOD_LIST_TABLE(process_watch_methods,
72     MRP_LUA_METHOD_CONSTRUCTOR(process_watch_no_constructor)
73     MRP_LUA_METHOD(delete, process_watch_delete));
74
75 MRP_LUA_METHOD_LIST_TABLE(process_watch_overrides,
76     MRP_LUA_OVERRIDE_CALL(process_watch_no_constructor));
77
78 MRP_LUA_MEMBER_LIST_TABLE(process_watch_members,
79     MRP_LUA_CLASS_INTEGER("events", 0, setmember, getmember, RO)
80     MRP_LUA_CLASS_ANY    ("filter", 0, setmember, getmember, RO)
81     MRP_LUA_CLASS_ANY    ("notify", 0, setmember, getmember, RO));
82
83 MRP_LUA_DEFINE_CLASS(process_watch, lua, process_watch_lua_t,
84                      process_watch_destroy, process_watch_methods,
85                      process_watch_overrides, process_watch_members, NULL,
86                      process_watch_changed, process_watch_tostring, NULL,
87                      MRP_LUA_CLASS_EXTENSIBLE);
88
89 MRP_LUA_CLASS_CHECKER(process_watch_lua_t, process_watch_lua,
90                       PROCESS_WATCH_LUA_CLASS);
91
92 typedef enum {
93     PROCESS_WATCH_MEMBER_EVENTS,
94     PROCESS_WATCH_MEMBER_FILTER,
95     PROCESS_WATCH_MEMBER_NOTIFY
96 } process_watch_member_t;
97
98
99 /*
100  * process event object
101  */
102
103 #define PROCESS_EVENT_CLASS MRP_LUA_CLASS_SIMPLE(ProcessEvent)
104
105 typedef struct {
106     mrp_proc_event_t *event;
107 } ProcessEvent_t;
108
109 typedef ProcessEvent_t process_event_lua_t;
110
111 static int event_no_constructor(lua_State *L);
112 static void *event_create(lua_State *L, mrp_proc_event_t *e);
113 static int event_setfield(lua_State *L);
114 static int event_getfield(lua_State *L);
115 static void event_destroy(void *data);
116 static process_event_lua_t *event_check(lua_State *L, int idx);
117 static bool register_event_class(lua_State *L);
118
119 MRP_LUA_CLASS_DEF_SIMPLE(
120     ProcessEvent,
121     ProcessEvent_t,
122     event_destroy,
123     MRP_LUA_METHOD_LIST(
124         MRP_LUA_METHOD_CONSTRUCTOR(event_no_constructor)),
125     MRP_LUA_METHOD_LIST(
126         MRP_LUA_OVERRIDE_CALL(event_no_constructor)
127         MRP_LUA_OVERRIDE_GETFIELD(event_getfield)
128         MRP_LUA_OVERRIDE_SETFIELD(event_setfield)));
129
130 static int process_watch_no_constructor(lua_State *L)
131 {
132     return luaL_error(L, "trying to create a process watch via constructor.");
133 }
134
135
136 static void process_watch_notify(process_watch_lua_t *w, lua_State *L,
137                                  mrp_proc_event_t *e)
138 {
139     mrp_funcbridge_value_t args[3], rv;
140     char                            rt;
141
142     if (L == NULL || w->notify == NULL)
143         return;
144
145     mrp_debug("notifying process watch %p", w);
146
147     args[0].pointer = w;
148     args[1].pointer = event_create(L, e);
149
150     if (!mrp_funcbridge_call_from_c(L, w->notify, "OO", &args[0], &rt, &rv)) {
151         mrp_log_error("Failed to notify process watch %p.", w);
152         mrp_free((char *)rv.string);
153     }
154 }
155
156
157 static void event_cb(mrp_proc_watch_t *pw, mrp_proc_event_t *e, void *user_data)
158 {
159     process_watch_lua_t *w = (process_watch_lua_t *)user_data;
160
161     MRP_UNUSED(pw);
162
163     mrp_debug("got notification (0x%x) for process watch %p", e->type, w);
164
165     process_watch_notify(w, mrp_lua_get_lua_state(), e);
166 }
167
168
169 process_watch_lua_t *process_watch_create(sysmon_lua_t *sm, lua_State *L)
170 {
171     process_watch_lua_t *w;
172     mrp_context_t       *ctx;
173     mrp_proc_filter_t   *f;
174     char                 e[256];
175
176     luaL_checktype(L, 2, LUA_TTABLE);
177
178     w = (process_watch_lua_t *)mrp_lua_create_object(L, PROCESS_WATCH_LUA_CLASS,
179                                                      NULL, 0);
180
181     mrp_list_init(&w->hook);
182     w->sysmon     = sm;
183     w->watchref   = LUA_NOREF;
184     w->filterref  = LUA_NOREF;
185     w->filter.uid = -1;
186     w->filter.gid = -1;
187
188     if (mrp_lua_init_members(w, L, 2, e, sizeof(e)) != 1) {
189         luaL_error(L, "failed to initialize process watch (error: %s)",
190                    *e ? e : "<unknown error>");
191         return NULL;
192     }
193
194     if (w->notify == NULL) {
195         luaL_error(L, "process watch notification callback not set");
196         return NULL;
197     }
198
199     if (w->filter.pid != 0 || w->filter.path || w->filter.comm ||
200         w->filter.uid != (uid_t)-1 || w->filter.gid != (gid_t)-1)
201         f = &w->filter;
202     else
203         f = NULL;
204
205     ctx  = mrp_lua_get_murphy_context();
206     w->w = mrp_add_proc_watch(ctx->ml, w->mask, f, event_cb, w);
207
208     if (w->w != NULL)
209         return w;
210     else {
211         luaL_error(L, "failed to create process watch");
212         return NULL;
213     }
214 }
215
216
217 static void process_watch_destroy(void *data)
218 {
219     mrp_debug("process watch %p destroyed", data);
220 }
221
222
223 static int process_watch_delete(lua_State *L)
224 {
225     process_watch_lua_t *w = process_watch_lua_check(L, 1);
226
227     mrp_lua_object_unref_value(w, L, w->filterref);
228     w->filterref = LUA_NOREF;
229
230     mrp_del_proc_watch(w->w);
231     sysmon_del_process_watch(w->sysmon, w);
232
233     return 0;
234 }
235
236
237 static void process_watch_changed(void *data, lua_State *L, int member)
238 {
239     MRP_UNUSED(data);
240     MRP_UNUSED(L);
241     MRP_UNUSED(member);
242 }
243
244
245 static int process_watch_setmember(void *data, lua_State *L, int member,
246                                    mrp_lua_value_t *v)
247 {
248     process_watch_lua_t *w = (process_watch_lua_t *)data;
249
250     switch (member) {
251     case PROCESS_WATCH_MEMBER_EVENTS:
252         w->mask = v->s32;
253         return 1;
254
255     case PROCESS_WATCH_MEMBER_FILTER:
256         if (v->any != LUA_NOREF)
257             return setup_filter(w, L, v->any);
258         else
259             return 1;
260
261     case PROCESS_WATCH_MEMBER_NOTIFY:
262         if (!mrp_lua_object_deref_value(w, L, v->any, false))
263             return 0;
264         switch (lua_type(L, -1)) {
265         case LUA_TFUNCTION:
266             w->notify = mrp_funcbridge_create_luafunc(L, -1);
267             break;
268         default:
269             w->notify = NULL;
270             break;
271         }
272         lua_pop(L, 1);
273         mrp_lua_object_unref_value(w, L, v->any);
274
275         return (w->notify != NULL ? 1 : 0);
276
277     default:
278         mrp_log_error("Can't set read-only process watch member #%d.", member);
279         return 0;
280     }
281 }
282
283
284 static int process_watch_getmember(void *data, lua_State *L, int member,
285                                    mrp_lua_value_t *v)
286 {
287     process_watch_lua_t *w = (process_watch_lua_t *)data;
288
289     MRP_UNUSED(data);
290     MRP_UNUSED(L);
291
292     switch (member) {
293     case PROCESS_WATCH_MEMBER_EVENTS:
294         v->s32 = w->mask;
295         return 1;
296
297     case PROCESS_WATCH_MEMBER_FILTER:
298         v->any = w->filterref;
299         return 1;
300
301     default:
302         v->any = LUA_REFNIL;
303         return 1;
304     }
305 }
306
307
308 static ssize_t process_watch_tostring(mrp_lua_tostr_mode_t mode, char *buf,
309                                       size_t size, lua_State *L, void *data)
310 {
311     process_watch_lua_t *w = (process_watch_lua_t *)data;
312
313     MRP_UNUSED(L);
314
315     switch (mode & MRP_LUA_TOSTR_MODEMASK) {
316     case MRP_LUA_TOSTR_LUA:
317     default:
318         return snprintf(buf, size, "{process watch %p}", w);
319     }
320 }
321
322
323 int process_event_mask(lua_State *L, int idx)
324 {
325     mrp_proc_event_type_t  mask;
326     const char            *event, *name;
327     int                    ktype, type, i;
328     size_t                 len;
329
330     mask = 0;
331     MRP_LUA_FOREACH_ALL(L, i, idx, ktype, name, len) {
332         if (ktype != LUA_TNUMBER || (type = lua_type(L, -1)) != LUA_TSTRING) {
333             mrp_log_warning("ignoring invalid event (0x%x: 0x%x)", ktype,
334                             ktype == LUA_TNUMBER ? type : 0);
335             continue;
336         }
337         else
338             event = lua_tolstring(L, -1, &len);
339
340         switch (len) {
341         case 3:
342             if (!strcmp(event, "uid"))
343                 mask |= MRP_PROC_EVENT_UID;
344             else if (!strcmp(event, "gid"))
345                 mask |= MRP_PROC_EVENT_GID;
346             else if (!strcmp(event, "sid"))
347                 mask |= MRP_PROC_EVENT_SID;
348             else
349                 goto unknown;
350             break;
351
352         case 4:
353             if (!strcmp(event, "none"))
354                 mask |= MRP_PROC_EVENT_NONE;
355             else if (!strcmp(event, "fork"))
356                 mask |= MRP_PROC_EVENT_FORK;
357             else if (!strcmp(event, "exec"))
358                 mask |= MRP_PROC_EVENT_EXEC;
359             else if (!strcmp(event, "exit"))
360                 mask |= MRP_PROC_EVENT_EXIT;
361             else if (!strcmp(event, "user"))
362                 mask |= MRP_PROC_EVENT_UID;
363             else if (!strcmp(event, "comm"))
364                 mask |= MRP_PROC_EVENT_COMM;
365             else if (!strcmp(event, "core"))
366                 mask |= MRP_PROC_EVENT_COREDUMP;
367             else
368                 goto unknown;
369             break;
370
371         case 5:
372             if (!strcmp(event, "trace"))
373                 mask |= MRP_PROC_EVENT_PTRACE;
374             else if (!strcmp(event, "group"))
375                 mask |= MRP_PROC_EVENT_GID;
376             else
377                 goto unknown;
378             break;
379
380         case 6:
381             if (!strcmp(event, "ptrace"))
382                 mask |= MRP_PROC_EVENT_PTRACE;
383             else
384                 goto unknown;
385             break;
386
387         case 7:
388             if (!strcmp(event, "session"))
389                 mask |= MRP_PROC_EVENT_SID;
390             else if (!strcmp(event, "recurse"))
391                 mask |= MRP_PROC_RECURSE;
392             else
393                 goto unknown;
394             break;
395
396         case 8:
397             if (!strcmp(event, "coredump"))
398                 mask |= MRP_PROC_EVENT_COREDUMP;
399             else
400                 goto unknown;
401             break;
402
403         case 9:
404             if (!strcmp(event, "nothreads"))
405                 mask |= MRP_PROC_IGNORE_THREADS;
406             else
407                 goto unknown;
408             break;
409
410         case 13:
411             if (!strcmp(event, "ignorethreads"))
412                 mask |= MRP_PROC_IGNORE_THREADS;
413             else
414                 goto unknown;
415             break;
416
417         unknown:
418         default:
419             mrp_log_warning("ignoring unknown process event '%s'", event);
420             break;
421         }
422     }
423
424     lua_pushinteger(L, mask);
425     return 1;
426 }
427
428
429 static int setup_filter(process_watch_lua_t *w, lua_State *L, int filterref)
430 {
431     int         top = lua_gettop(L);
432     int         ktype, i;
433     const char *kname, *usr, *grp;
434     size_t      klen, len;
435
436     if (!mrp_lua_object_deref_value(w, L, filterref, false)) {
437         mrp_log_error("Failed to dereference process watch filter table.");
438         return 0;
439     }
440
441     MRP_LUA_FOREACH_ALL(L, i, top + 1, ktype, kname, klen) {
442         if (ktype != LUA_TSTRING) {
443             mrp_log_error("Invalid process watch filter (non-string key).");
444             goto fail;
445         }
446
447         kname = lua_tolstring(L, -2, &len);
448
449         if (!strcmp(kname, "pid") || !strcmp(kname, "process")) {
450             if (lua_type(L, -1) != LUA_TNUMBER) {
451                 mrp_log_error("Invalid process watch filter pid.");
452                 goto fail;
453             }
454
455             w->filter.pid = lua_tointeger(L, -1);
456             continue;
457         }
458
459         if (!strcmp(kname, "path")) {
460             if (lua_type(L, -1) != LUA_TSTRING) {
461                 mrp_log_error("Invalid process watch filter path.");
462                 goto fail;
463             }
464
465             w->filter.path = mrp_strdup(lua_tostring(L, -1));
466             continue;
467         }
468
469         if (!strcmp(kname, "comm") || !strcmp(kname, "name")) {
470             if (lua_type(L, -1) != LUA_TSTRING) {
471                 mrp_log_error("Invalid process watch filter comm.");
472                 goto fail;
473             }
474
475             w->filter.comm = mrp_strdup(lua_tostring(L, -1));
476             continue;
477         }
478
479         if (!strcmp(kname, "uid") || !strcmp(kname, "user")) {
480             if (lua_type(L, -1) == LUA_TNUMBER) {
481                 w->filter.uid = lua_tointeger(L, -1);
482                 continue;
483             }
484
485             if (lua_type(L, -1) == LUA_TSTRING) {
486                 struct passwd *pw = getpwnam(usr = lua_tolstring(L, -1, &len));
487
488                 if (pw == NULL) {
489                     mrp_log_error("Unknown process watch filter user '%s'.",
490                                   usr);
491                     goto fail;
492                 }
493
494                 w->filter.uid = pw->pw_uid;
495                 continue;
496             }
497
498             mrp_log_error("Invalid process watch filter user.");
499             goto fail;
500         }
501
502         if (!strcmp(kname, "gid") || !strcmp(kname, "group")) {
503             if (lua_type(L, -1) == LUA_TNUMBER) {
504                 w->filter.gid = lua_tointeger(L, -1);
505                 continue;
506             }
507
508             if (lua_type(L, -1) == LUA_TSTRING) {
509                 struct group *gr = getgrnam(grp = lua_tolstring(L, -1, &len));
510
511                 if (gr == NULL) {
512                     mrp_log_error("Unknown process watch filter group '%s'.",
513                                   grp);
514                     goto fail;
515                 }
516
517                 w->filter.gid = gr->gr_gid;
518                 continue;
519             }
520
521             mrp_log_error("Invalid process watch filter group.");
522             goto fail;
523         }
524
525         mrp_log_error("Invalid process watch filter field '%s'.", kname);
526         goto fail;
527     }
528
529     w->filterref = mrp_lua_object_ref_value(w, L, top + 1);
530     lua_settop(L, top);
531
532     return 1;
533
534  fail:
535     lua_settop(L, top);
536     return 0;
537 }
538
539
540 static bool register_event_class(lua_State *L)
541 {
542     mrp_lua_create_object_class(L, PROCESS_EVENT_CLASS);
543
544     return true;
545 }
546
547
548 static int event_no_constructor(lua_State *L)
549 {
550     return luaL_error(L, "trying to create a process event via constructor.");
551 }
552
553
554 static void *event_create(lua_State *L, mrp_proc_event_t *event)
555 {
556     static bool          eclass = false;
557     process_event_lua_t *e;
558
559     if (!eclass)
560         if (!(eclass = register_event_class(L)))
561             return NULL;
562
563     e = (process_event_lua_t *)mrp_lua_create_object(L, PROCESS_EVENT_CLASS,
564                                                      NULL, 0);
565
566     if (e == NULL || (e->event = mrp_allocz(sizeof(*e->event))) == NULL) {
567         mrp_log_error("Failed to allocate process event.");
568         return NULL;
569     }
570
571     memcpy(e->event, event, sizeof(*e->event));
572
573     return e;
574 }
575
576
577 static int event_setfield(lua_State *L)
578 {
579     event_check(L, 1);
580     return luaL_error(L, "trying to set field on read-only process event");
581 }
582
583
584 static int event_getfield(lua_State *L)
585 {
586     process_event_lua_t *e = event_check(L, -2);
587     const char          *name, *type;;
588
589     if (lua_type(L, -1) != LUA_TSTRING)
590         return luaL_error(L, "process event has only string fields");
591
592     if (e->event == NULL) {
593         lua_pushnil(L);
594         return 1;
595     }
596
597     name = lua_tostring(L, -1);
598
599     if (!strcmp(name, "type")) {
600         switch (e->event->type) {
601         case MRP_PROC_EVENT_NONE:     type = "<none>"   ; break;
602         case MRP_PROC_EVENT_FORK:     type = "fork"     ; break;
603         case MRP_PROC_EVENT_EXEC:     type = "exec"     ; break;
604         case MRP_PROC_EVENT_EXIT:     type = "exit"     ; break;
605         case MRP_PROC_EVENT_UID:      type = "uid"      ; break;
606         case MRP_PROC_EVENT_GID:      type = "gid"      ; break;
607         case MRP_PROC_EVENT_SID:      type = "sid"      ; break;
608         case MRP_PROC_EVENT_PTRACE:   type = "ptrace"   ; break;
609         case MRP_PROC_EVENT_COMM:     type = "comm"     ; break;
610         case MRP_PROC_EVENT_COREDUMP: type = "coredump" ; break;
611         default:                      type = "<unknown>"; break;
612         }
613
614         lua_pushstring(L, type);
615         return 1;
616     }
617
618     if (!strcmp(name, "pid") || !strcmp(name, "process_pid")) {
619         if (e->event->type != MRP_PROC_EVENT_FORK)
620             lua_pushinteger(L, e->event->raw->event_data.exec.process_pid);
621         else
622             lua_pushnil(L);
623         return 1;
624     }
625
626     if (!strcmp(name, "tgid") || !strcmp(name, "process_tgid")) {
627         if (e->event->type != MRP_PROC_EVENT_FORK)
628             lua_pushinteger(L, e->event->raw->event_data.exec.process_tgid);
629         else
630             lua_pushnil(L);
631         return 1;
632     }
633
634     if (e->event->type == MRP_PROC_EVENT_FORK) {
635         if (!strcmp(name, "parent_pid"))
636             lua_pushinteger(L, e->event->raw->event_data.fork.parent_pid);
637         else if (!strcmp(name, "parent_tgid"))
638             lua_pushinteger(L, e->event->raw->event_data.fork.parent_tgid);
639         else if (!strcmp(name, "child_pid"))
640             lua_pushinteger(L, e->event->raw->event_data.fork.child_pid);
641         else if (!strcmp(name, "child_tgid"))
642             lua_pushinteger(L, e->event->raw->event_data.fork.child_tgid);
643         else
644             lua_pushnil(L);
645         return 1;
646     }
647
648     if (e->event->type == MRP_PROC_EVENT_UID) {
649         if (!strcmp(name, "ruid"))
650             lua_pushinteger(L, e->event->raw->event_data.id.r.ruid);
651         else if (!strcmp(name, "euid"))
652             lua_pushinteger(L, e->event->raw->event_data.id.e.euid);
653         else
654             lua_pushnil(L);
655         return 1;
656     }
657
658     if (e->event->type == MRP_PROC_EVENT_GID) {
659         if (!strcmp(name, "rgid"))
660             lua_pushinteger(L, e->event->raw->event_data.id.r.rgid);
661         else if (!strcmp(name, "egid"))
662             lua_pushinteger(L, e->event->raw->event_data.id.e.egid);
663         else
664             lua_pushnil(L);
665         return 1;
666     }
667
668     if (e->event->type == MRP_PROC_EVENT_PTRACE) {
669         if (!strcmp(name, "tracer_pid"))
670             lua_pushinteger(L, e->event->raw->event_data.ptrace.tracer_pid);
671         else if (!strcmp(name, "tracer_tgid"))
672             lua_pushinteger(L, e->event->raw->event_data.ptrace.tracer_tgid);
673         else
674             lua_pushnil(L);
675         return 1;
676     }
677
678     if (e->event->type == MRP_PROC_EVENT_COMM) {
679         if (!strcmp(name, "comm"))
680             lua_pushstring(L, e->event->raw->event_data.comm.comm);
681         else
682             lua_pushnil(L);
683         return 1;
684     }
685
686     if (e->event->type == MRP_PROC_EVENT_EXIT) {
687         if (!strcmp(name, "exit_code") || !strcmp(name, "code"))
688             lua_pushinteger(L, e->event->raw->event_data.exit.exit_code);
689         else if (!strcmp(name, "exit_signal") || !strcmp(name, "signal"))
690             lua_pushinteger(L, e->event->raw->event_data.exit.exit_signal);
691         else
692             lua_pushnil(L);
693         return 1;
694     }
695
696     lua_pushnil(L);
697     return 0;
698 }
699
700
701 static void event_destroy(void *data)
702 {
703     process_event_lua_t *e = (process_event_lua_t *)data;
704
705     mrp_free(e->event);
706     e->event = NULL;
707 }
708
709 static process_event_lua_t *event_check(lua_State *L, int idx)
710 {
711     return (process_event_lua_t *)
712         mrp_lua_check_object(L, PROCESS_EVENT_CLASS, idx);
713 }