Skip to content

Commit 99ec1f9

Browse files
oleavrhsorbo
andcommitted
gmain: Use kqueue when available
To fit in better with APIs used by Apple in sandboxed system processes, so Frida users don't always have to jump through hoops to modify sandbox profiles or suspend the sandbox temporarily. Co-authored-by: Håvard Sørbø <havard@hsorbo.no>
1 parent 2255d78 commit 99ec1f9

File tree

7 files changed

+297
-1
lines changed

7 files changed

+297
-1
lines changed

glib/giochannel.h

+2
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,8 @@ gint g_io_channel_unix_get_fd (GIOChannel *channel);
329329
/* Hook for GClosure / GSource integration. Don't touch */
330330
GLIB_VAR GSourceFuncs g_io_watch_funcs;
331331

332+
#define G_KQUEUE_WAKEUP_HANDLE -42
333+
332334
#ifdef G_OS_WIN32
333335

334336
/* You can use this "pseudo file descriptor" in a GPollFD to add

glib/glibconfig.h.in

+1
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ typedef gint64 goffset;
101101
#define G_GOFFSET_FORMAT G_GINT64_FORMAT
102102
#define G_GOFFSET_CONSTANT(val) G_GINT64_CONSTANT(val)
103103

104+
@glib_pollfd_flavor@
104105
#define G_POLLFD_FORMAT @g_pollfd_format@
105106

106107
#define GPOINTER_TO_INT(p) ((gint) @glib_gpi_cast@ (p))

glib/gmain.c

+223
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,10 @@
8383
#include <mach/mach_time.h>
8484
#endif
8585

86+
#ifdef HAVE_KQUEUE
87+
#include <sys/event.h>
88+
#endif
89+
8690
#include "glib_trace.h"
8791

8892
#include "gmain.h"
@@ -326,6 +330,10 @@ struct _GMainContext
326330

327331
gint64 time;
328332
gboolean time_is_fresh;
333+
334+
#ifdef HAVE_KQUEUE
335+
gint kq;
336+
#endif
329337
};
330338

331339
struct _GSourceCallback
@@ -599,6 +607,10 @@ _g_main_recover_from_fork_in_child (void)
599607
{
600608
GMainContext *context = l->data;
601609

610+
#ifdef HAVE_KQUEUE
611+
context->kq = kqueue ();
612+
#endif
613+
602614
g_wakeup_free (context->wakeup);
603615
context->wakeup = g_wakeup_new ();
604616

@@ -716,6 +728,10 @@ g_main_context_unref (GMainContext *context)
716728

717729
poll_rec_list_free (context, context->poll_records);
718730

731+
#ifdef HAVE_KQUEUE
732+
close (context->kq);
733+
#endif
734+
719735
g_wakeup_free (context->wakeup);
720736
g_cond_clear (&context->cond);
721737

@@ -812,6 +828,10 @@ g_main_context_new_with_flags (GMainContextFlags flags)
812828
context->pending_dispatches = g_ptr_array_new ();
813829

814830
context->time_is_fresh = FALSE;
831+
832+
#ifdef HAVE_KQUEUE
833+
context->kq = kqueue ();
834+
#endif
815835

816836
context->wakeup = g_wakeup_new ();
817837
g_wakeup_get_pollfd (context->wakeup, &context->wake_up_rec);
@@ -4611,6 +4631,9 @@ g_main_context_poll (GMainContext *context,
46114631
if (n_fds || timeout != 0)
46124632
{
46134633
int ret, errsv;
4634+
#ifdef HAVE_KQUEUE
4635+
guint max_events;
4636+
#endif
46144637

46154638
#ifdef G_MAIN_POLL_DEBUG
46164639
poll_timer = NULL;
@@ -4625,8 +4648,100 @@ g_main_context_poll (GMainContext *context,
46254648
LOCK_CONTEXT (context);
46264649

46274650
poll_func = context->poll_func;
4651+
#ifdef HAVE_KQUEUE
4652+
max_events = context->n_poll_records;
4653+
#endif
46284654

46294655
UNLOCK_CONTEXT (context);
4656+
4657+
#ifdef HAVE_KQUEUE
4658+
if (poll_func == g_poll)
4659+
{
4660+
struct kevent *events;
4661+
struct timespec *ts, ts_storage;
4662+
4663+
events = g_newa (struct kevent, max_events);
4664+
4665+
if (timeout != -1)
4666+
{
4667+
ts_storage.tv_sec = timeout / 1000;
4668+
ts_storage.tv_nsec = (timeout % 1000) * 1000000;
4669+
ts = &ts_storage;
4670+
}
4671+
else
4672+
{
4673+
ts = NULL;
4674+
}
4675+
4676+
ret = kevent (context->kq, NULL, 0, events, max_events, ts);
4677+
errsv = errno;
4678+
4679+
if (ret > 0)
4680+
{
4681+
int i;
4682+
4683+
for (int i = 0; i < n_fds; i++)
4684+
fds[i].revents = 0;
4685+
4686+
for (i = 0; i < ret; i++)
4687+
{
4688+
struct kevent *ev = &events[i];
4689+
4690+
for (int i = 0; i < n_fds; i++)
4691+
{
4692+
GPollFD *pfd = &fds[i];
4693+
4694+
if (pfd->fd == (gint) ev->ident)
4695+
{
4696+
switch (ev->filter)
4697+
{
4698+
case EVFILT_READ:
4699+
if (pfd->events & G_IO_IN)
4700+
pfd->revents |= G_IO_IN;
4701+
#ifdef EV_OOBAND
4702+
if (pfd->events & G_IO_PRI && ev->flags & EV_OOBAND)
4703+
pfd->revents |= G_IO_PRI;
4704+
#endif
4705+
if (ev->flags & EV_EOF)
4706+
{
4707+
pfd->revents |= G_IO_HUP;
4708+
if (ev->fflags != 0)
4709+
pfd->revents |= G_IO_ERR;
4710+
}
4711+
break;
4712+
case EVFILT_WRITE:
4713+
if (pfd->events & G_IO_OUT)
4714+
pfd->revents |= G_IO_OUT;
4715+
if (ev->flags & EV_EOF)
4716+
pfd->revents |= G_IO_ERR;
4717+
break;
4718+
#ifdef EVFILT_EXCEPT
4719+
case EVFILT_EXCEPT:
4720+
if (pfd->events & G_IO_PRI)
4721+
pfd->revents |= G_IO_PRI;
4722+
if (ev->flags & EV_EOF)
4723+
pfd->revents |= G_IO_HUP;
4724+
break;
4725+
#endif
4726+
case EVFILT_USER:
4727+
if (pfd->events & G_IO_IN)
4728+
pfd->revents |= G_IO_IN;
4729+
break;
4730+
}
4731+
}
4732+
}
4733+
}
4734+
}
4735+
else if (ret < 0 && errsv != EINTR)
4736+
{
4737+
g_warning ("kevent(2) failed due to: %s.",
4738+
g_strerror (errsv));
4739+
}
4740+
4741+
goto out;
4742+
}
4743+
#endif
4744+
46304745
ret = (*poll_func) (fds, n_fds, timeout);
46314746
errsv = errno;
46324747
if (ret < 0 && errsv != EINTR)
@@ -4639,6 +4754,11 @@ g_main_context_poll (GMainContext *context,
46394754
#endif
46404755
}
46414756

4757+
4758+
#ifdef HAVE_KQUEUE
4759+
out:
4760+
;
4761+
#endif
46424762
#ifdef G_MAIN_POLL_DEBUG
46434763
if (_g_main_poll_debug)
46444764
{
@@ -4756,6 +4876,43 @@ g_main_context_add_poll_unlocked (GMainContext *context,
47564876

47574877
context->poll_changed = TRUE;
47584878

4879+
#ifdef HAVE_KQUEUE
4880+
{
4881+
struct kevent events[3], *ev;
4882+
4883+
ev = events;
4884+
if (fd->fd == G_KQUEUE_WAKEUP_HANDLE)
4885+
{
4886+
*(fd->kq) = context->kq;
4887+
4888+
EV_SET (ev, fd->fd, EVFILT_USER, EV_ADD, NOTE_FFCOPY, 0, NULL);
4889+
ev++;
4890+
}
4891+
else
4892+
{
4893+
if (fd->events & G_IO_IN)
4894+
{
4895+
EV_SET (ev, fd->fd, EVFILT_READ, EV_ADD, 0, 0, NULL);
4896+
ev++;
4897+
}
4898+
if (fd->events & G_IO_OUT)
4899+
{
4900+
EV_SET (ev, fd->fd, EVFILT_WRITE, EV_ADD, 0, 0, NULL);
4901+
ev++;
4902+
}
4903+
#ifdef EVFILT_EXCEPT
4904+
if (fd->events & G_IO_PRI)
4905+
{
4906+
EV_SET (ev, fd->fd, EVFILT_EXCEPT, EV_ADD, NOTE_OOB, 0, NULL);
4907+
ev++;
4908+
}
4909+
#endif
4910+
}
4911+
4912+
kevent (context->kq, events, ev - events, NULL, 0, NULL);
4913+
}
4914+
#endif
4915+
47594916
/* Now wake up the main loop if it is waiting in the poll() */
47604917
if (fd != &context->wake_up_rec)
47614918
g_wakeup_signal (context->wakeup);
@@ -4817,6 +4974,72 @@ g_main_context_remove_poll_unlocked (GMainContext *context,
48174974

48184975
context->poll_changed = TRUE;
48194976

4977+
#ifdef HAVE_KQUEUE
4978+
{
4979+
gboolean remove_wakeup, remove_in, remove_out, remove_pri;
4980+
struct kevent events[3], *ev;
4981+
4982+
remove_wakeup = fd->fd == G_KQUEUE_WAKEUP_HANDLE;
4983+
if (remove_wakeup)
4984+
{
4985+
remove_in = FALSE;
4986+
remove_out = FALSE;
4987+
remove_pri = FALSE;
4988+
}
4989+
else
4990+
{
4991+
remove_in = !!(fd->events & G_IO_IN);
4992+
remove_out = !!(fd->events & G_IO_OUT);
4993+
remove_pri = !!(fd->events & G_IO_PRI);
4994+
}
4995+
4996+
for (pollrec = context->poll_records; pollrec; pollrec = pollrec->next)
4997+
{
4998+
GPollFD *cur = pollrec->fd;
4999+
5000+
if (cur->fd == G_KQUEUE_WAKEUP_HANDLE)
5001+
{
5002+
remove_wakeup = FALSE;
5003+
}
5004+
else if (cur->fd == fd->fd)
5005+
{
5006+
if (cur->events & G_IO_IN)
5007+
remove_in = FALSE;
5008+
if (cur->events & G_IO_OUT)
5009+
remove_out = FALSE;
5010+
if (cur->events & G_IO_PRI)
5011+
remove_pri = FALSE;
5012+
}
5013+
}
5014+
5015+
ev = events;
5016+
if (remove_wakeup)
5017+
{
5018+
EV_SET (ev, fd->fd, EVFILT_USER, EV_DELETE, 0, 0, NULL);
5019+
ev++;
5020+
}
5021+
if (remove_in)
5022+
{
5023+
EV_SET (ev, fd->fd, EVFILT_READ, EV_DELETE, 0, 0, NULL);
5024+
ev++;
5025+
}
5026+
if (remove_out)
5027+
{
5028+
EV_SET (ev, fd->fd, EVFILT_WRITE, EV_DELETE, 0, 0, NULL);
5029+
ev++;
5030+
}
5031+
#ifdef EVFILT_EXCEPT
5032+
if (remove_pri)
5033+
{
5034+
EV_SET (ev, fd->fd, EVFILT_EXCEPT, EV_DELETE, 0, 0, NULL);
5035+
ev++;
5036+
}
5037+
#endif
5038+
5039+
kevent (context->kq, events, ev - events, NULL, 0, NULL);
5040+
}
5041+
#endif
5042+
48205043
/* Now wake up the main loop if it is waiting in the poll() */
48215044
g_wakeup_signal (context->wakeup);
48225045
}

glib/gpoll.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@
8080
extern gboolean _g_main_poll_debug;
8181
#endif
8282

83-
#ifdef HAVE_POLL
83+
#if defined (HAVE_POLL) && !defined (HAVE_KQUEUE)
8484

8585
/**
8686
* g_poll:

glib/gpoll.h

+3
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,9 @@ struct _GPollFD
101101
#endif
102102
gushort events;
103103
gushort revents;
104+
#ifdef G_POLLFD_KQUEUE
105+
gint *kq;
106+
#endif
104107
};
105108

106109
/**

glib/gwakeup.c

+59
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,65 @@ g_wakeup_free (GWakeup *wakeup)
114114
CloseHandle ((HANDLE) wakeup);
115115
}
116116

117+
#elif defined (HAVE_KQUEUE)
118+
119+
#include "glib-unix.h"
120+
121+
#include <sys/event.h>
122+
123+
struct _GWakeup
124+
{
125+
gint kq;
126+
};
127+
128+
GWakeup *
129+
g_wakeup_new (void)
130+
{
131+
GWakeup *wakeup;
132+
133+
wakeup = g_slice_new (GWakeup);
134+
wakeup->kq = -1;
135+
136+
return wakeup;
137+
}
138+
139+
void
140+
g_wakeup_get_pollfd (GWakeup *wakeup,
141+
GPollFD *poll_fd)
142+
{
143+
poll_fd->fd = G_KQUEUE_WAKEUP_HANDLE;
144+
poll_fd->events = G_IO_IN;
145+
poll_fd->kq = &wakeup->kq;
146+
}
147+
148+
void
149+
g_wakeup_acknowledge (GWakeup *wakeup)
150+
{
151+
struct kevent ev[2];
152+
153+
EV_SET (&ev[0], G_KQUEUE_WAKEUP_HANDLE, EVFILT_USER, EV_DELETE,
154+
0, 0, NULL);
155+
EV_SET (&ev[1], G_KQUEUE_WAKEUP_HANDLE, EVFILT_USER, EV_ADD,
156+
NOTE_FFCOPY, 0, NULL);
157+
kevent (wakeup->kq, ev, G_N_ELEMENTS (ev), NULL, 0, NULL);
158+
}
159+
160+
void
161+
g_wakeup_signal (GWakeup *wakeup)
162+
{
163+
struct kevent ev;
164+
165+
EV_SET (&ev, G_KQUEUE_WAKEUP_HANDLE, EVFILT_USER, 0, NOTE_TRIGGER, 0, NULL);
166+
167+
kevent (wakeup->kq, &ev, 1, NULL, 0, NULL);
168+
}
169+
170+
void
171+
g_wakeup_free (GWakeup *wakeup)
172+
{
173+
g_slice_free (GWakeup, wakeup);
174+
}
175+
117176
#else
118177

119178
#include "glib-unix.h"

0 commit comments

Comments
 (0)