1/*
2 * Copyright (c) 2009 Mark Heily <mark@heily.com>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17#include <errno.h>
18#include <err.h>
19#include <fcntl.h>
20#include <pthread.h>
21#include <signal.h>
22#include <stdlib.h>
23#include <stdio.h>
24#include <sys/queue.h>
25#include <sys/types.h>
26#include <sys/wait.h>
27#include <string.h>
28#include <unistd.h>
29
30#include <limits.h>
31
32#include "sys/event.h"
33#include "private.h"
34
35pthread_cond_t   wait_cond = PTHREAD_COND_INITIALIZER;
36pthread_mutex_t  wait_mtx = PTHREAD_MUTEX_INITIALIZER;
37
38struct evfilt_data {
39    pthread_t       wthr_id;
40};
41
42static void *
43wait_thread(void *arg)
44{
45    struct filter *filt = (struct filter *) arg;
46    struct knote *kn;
47    int status, result;
48    pid_t pid;
49    sigset_t sigmask;
50
51    /* Block all signals */
52    sigfillset (&sigmask);
53    sigdelset(&sigmask, SIGCHLD);
54    pthread_sigmask(SIG_BLOCK, &sigmask, NULL);
55
56    for (;;) {
57
58        /* Wait for a child process to exit(2) */
59        if ((pid = waitpid(-1, &status, 0)) < 0) {
60            if (errno == ECHILD) {
61                dbg_puts("got ECHILD, waiting for wakeup condition");
62                pthread_mutex_lock(&wait_mtx);
63                pthread_cond_wait(&wait_cond, &wait_mtx);
64                pthread_mutex_unlock(&wait_mtx);
65                dbg_puts("awoken from ECHILD-induced sleep");
66                continue;
67            }
68            if (errno == EINTR)
69                continue;
70            dbg_printf("wait(2): %s", strerror(errno));
71            break;
72        }
73
74        /* Create a proc_event */
75        if (WIFEXITED(status)) {
76            result = WEXITSTATUS(status);
77        } else if (WIFSIGNALED(status)) {
78            /* FIXME: probably not true on BSD */
79            result = WTERMSIG(status);
80        } else {
81            dbg_puts("unexpected code path");
82            result = 234;           /* arbitrary error value */
83        }
84
85        /* Scan the wait queue to see if anyone is interested */
86        pthread_mutex_lock(&filt->kf_mtx);
87        kn = knote_lookup(filt, pid);
88        if (kn != NULL) {
89            kn->kev.data = result;
90            kn->kev.fflags = NOTE_EXIT;
91            LIST_REMOVE(kn, entries);
92            LIST_INSERT_HEAD(&filt->kf_eventlist, kn, entries);
93            /* Indicate read(2) readiness */
94            /* TODO: error handling */
95            filter_raise(filt);
96        }
97        pthread_mutex_unlock(&filt->kf_mtx);
98    }
99
100    /* TODO: error handling */
101
102    return (NULL);
103}
104
105int
106evfilt_proc_init(struct filter *filt)
107{
108    struct evfilt_data *ed;
109
110    if ((ed = calloc(1, sizeof(*ed))) == NULL)
111        return (-1);
112
113    if (filter_socketpair(filt) < 0)
114        goto errout;
115    if (pthread_create(&ed->wthr_id, NULL, wait_thread, filt) != 0)
116        goto errout;
117
118    return (0);
119
120errout:
121    free(ed);
122    return (-1);
123}
124
125void
126evfilt_proc_destroy(struct filter *filt)
127{
128//TODO:    pthread_cancel(filt->kf_data->wthr_id);
129    close(filt->kf_pfd);
130}
131
132int
133evfilt_proc_copyin(struct filter *filt,
134        struct knote *dst, const struct kevent *src)
135{
136    if (src->flags & EV_ADD && KNOTE_EMPTY(dst)) {
137        memcpy(&dst->kev, src, sizeof(*src));
138        /* TODO: think about locking the mutex first.. */
139        pthread_cond_signal(&wait_cond);
140    }
141
142    if (src->flags & EV_ADD || src->flags & EV_ENABLE) {
143        /* Nothing to do.. */
144    }
145
146    return (0);
147}
148
149int
150evfilt_proc_copyout(struct filter *filt,
151            struct kevent *dst,
152            int maxevents)
153{
154    struct knote *kn;
155    int nevents = 0;
156
157    filter_lower(filt);
158
159    LIST_FOREACH(kn, &filt->kf_eventlist, entries) {
160        kevent_dump(&kn->kev);
161        memcpy(dst, &kn->kev, sizeof(*dst));
162        dst->fflags = NOTE_EXIT;
163
164        if (kn->kev.flags & EV_DISPATCH) {
165            KNOTE_DISABLE(kn);
166        }
167#if FIXME
168        /* XXX - NEED TO use safe foreach instead */
169        if (kn->kev.flags & EV_ONESHOT)
170            knote_free(kn);
171#endif
172
173        if (++nevents > maxevents)
174            break;
175        dst++;
176    }
177
178    if (!LIST_EMPTY(&filt->kf_eventlist))
179        filter_raise(filt);
180
181    return (nevents);
182}
183
184const struct filter evfilt_proc = {
185    EVFILT_PROC,
186    evfilt_proc_init,
187    evfilt_proc_destroy,
188    evfilt_proc_copyin,
189    evfilt_proc_copyout,
190};
191