xref: /php-src/sapi/fpm/fpm/events/port.c (revision ffc6f192)
1 /*
2    +----------------------------------------------------------------------+
3    | Copyright (c) The PHP Group                                          |
4    +----------------------------------------------------------------------+
5    | This source file is subject to version 3.01 of the PHP license,      |
6    | that is bundled with this package in the file LICENSE, and is        |
7    | available through the world-wide-web at the following url:           |
8    | https://www.php.net/license/3_01.txt                                 |
9    | If you did not receive a copy of the PHP license and are unable to   |
10    | obtain it through the world-wide-web, please send a note to          |
11    | license@php.net so we can mail you a copy immediately.               |
12    +----------------------------------------------------------------------+
13    | Authors: Jerome Loyet <jerome@loyet.net>                             |
14    +----------------------------------------------------------------------+
15 */
16 
17 #include "../fpm_config.h"
18 #include "../fpm_events.h"
19 #include "../fpm.h"
20 #include "../zlog.h"
21 
22 #ifdef HAVE_PORT_CREATE
23 
24 #include <port.h>
25 #include <poll.h>
26 #include <errno.h>
27 
28 static int fpm_event_port_init(int max);
29 static int fpm_event_port_clean(void);
30 static int fpm_event_port_wait(struct fpm_event_queue_s *queue, unsigned long int timeout);
31 static int fpm_event_port_add(struct fpm_event_s *ev);
32 static int fpm_event_port_remove(struct fpm_event_s *ev);
33 
34 static struct fpm_event_module_s port_module = {
35 	.name = "port",
36 	.support_edge_trigger = 0,
37 	.init = fpm_event_port_init,
38 	.clean = fpm_event_port_clean,
39 	.wait = fpm_event_port_wait,
40 	.add = fpm_event_port_add,
41 	.remove = fpm_event_port_remove,
42 };
43 
44 port_event_t *events = NULL;
45 int nevents = 0;
46 static int pfd = -1;
47 
48 #endif /* HAVE_PORT_CREATE */
49 
fpm_event_port_module(void)50 struct fpm_event_module_s *fpm_event_port_module(void) /* {{{ */
51 {
52 #ifdef HAVE_PORT_CREATE
53 	return &port_module;
54 #else
55 	return NULL;
56 #endif /* HAVE_PORT_CREATE */
57 }
58 /* }}} */
59 
60 #ifdef HAVE_PORT_CREATE
61 
62 /*
63  * Init the module
64  */
fpm_event_port_init(int max)65 static int fpm_event_port_init(int max) /* {{{ */
66 {
67 	/* open port */
68 	pfd = port_create();
69 	if (pfd < 0) {
70 		zlog(ZLOG_ERROR, "port: unable to initialize port_create()");
71 		return -1;
72 	}
73 
74 	if (max < 1) {
75 		return 0;
76 	}
77 
78 	/* alloc and clear active_pollfds */
79 	events = malloc(sizeof(port_event_t) * max);
80 	if (!events) {
81 		zlog(ZLOG_ERROR, "port: Unable to allocate %d events", max);
82 		return -1;
83 	}
84 
85 	nevents = max;
86 	return 0;
87 }
88 /* }}} */
89 
90 /*
91  * Clean the module
92  */
fpm_event_port_clean(void)93 static int fpm_event_port_clean(void)
94 {
95 	if (pfd > -1) {
96 		close(pfd);
97 		pfd = -1;
98 	}
99 
100 	if (events) {
101 		free(events);
102 		events = NULL;
103 	}
104 
105 	nevents = 0;
106 	return 0;
107 }
108 
109 /*
110  * wait for events or timeout
111  */
fpm_event_port_wait(struct fpm_event_queue_s * queue,unsigned long int timeout)112 static int fpm_event_port_wait(struct fpm_event_queue_s *queue, unsigned long int timeout) /* {{{ */
113 {
114 	int ret;
115 	unsigned int i, nget;
116 	timespec_t t;
117 
118 	/* convert timeout into timespec_t */
119 	t.tv_sec = (int)(timeout / 1000);
120 	t.tv_nsec = (timeout % 1000) * 1000 * 1000;
121 
122 	/* wait for incoming event or timeout. We want at least one event or timeout */
123 	nget = 1;
124 	events[0].portev_user = (void *)-1; /* so we can double check that an event was returned */
125 
126 	ret = port_getn(pfd, events, nevents, &nget, &t);
127 	if (ret < 0) {
128 
129 		/* trigger error unless signal interrupt or timeout */
130 		if (errno != EINTR && errno != ETIME) {
131 			zlog(ZLOG_WARNING, "poll() returns %d", errno);
132 			return -1;
133 		} else if (nget > 0 && events[0].portev_user == (void *)-1) {
134 			/* This confusing API can return an event at the same time
135 			 * that it reports EINTR or ETIME.  If that occurs, just
136 			 * report the event.  With EINTR, nget can be > 0 without
137 			 * any event, so check that portev_user was filled in.
138 			 *
139 			 * See discussion thread
140 			 *   http://marc.info/?l=opensolaris-networking-discuss&m=125071205204540
141 			 */
142 			nget = 0;
143 		}
144 	}
145 
146 	for (i = 0; i < nget; i++) {
147 		struct fpm_event_s *ev;
148 
149 		/* do we have a ptr to the event ? */
150 		if (!events[i].portev_user) {
151 			continue;
152 		}
153 
154 		ev = (struct fpm_event_s *)events[i].portev_user;
155 
156 		/* re-associate for next event */
157 		fpm_event_port_add(ev);
158 
159 		/* fire the event */
160 		fpm_event_fire(ev);
161 
162 		/* sanity check */
163 		if (fpm_globals.parent_pid != getpid()) {
164 			return -2;
165 		}
166 	}
167 	return nget;
168 }
169 /* }}} */
170 
171 /*
172  * Add a FD to the fd set
173  */
fpm_event_port_add(struct fpm_event_s * ev)174 static int fpm_event_port_add(struct fpm_event_s *ev) /* {{{ */
175 {
176 	/* add the event to port */
177 	if (port_associate(pfd, PORT_SOURCE_FD, ev->fd, POLLIN, (void *)ev) < 0) {
178 		zlog(ZLOG_ERROR, "port: unable to add the event");
179 		return -1;
180 	}
181 	return 0;
182 }
183 /* }}} */
184 
185 /*
186  * Remove a FD from the fd set
187  */
fpm_event_port_remove(struct fpm_event_s * ev)188 static int fpm_event_port_remove(struct fpm_event_s *ev) /* {{{ */
189 {
190 	/* remove the event from port */
191 	if (port_dissociate(pfd, PORT_SOURCE_FD, ev->fd) < 0) {
192 		zlog(ZLOG_ERROR, "port: unable to add the event");
193 		return -1;
194 	}
195 	return 0;
196 }
197 /* }}} */
198 
199 #endif /* HAVE_PORT_CREATE */
200