disabled logging by #define
[php-ancillary.git] / php-ancillary.c
1 #ifdef HAVE_CONFIG_H
2 #include "config.h"
3 #endif
4
5 #ifndef _XPG4_2 /* Solaris sucks */
6 # define _XPG4_2
7 #endif
8
9 #include <stdlib.h>
10 #include <string.h>
11 #include <sys/types.h>
12 #include <sys/socket.h>
13 #include <sys/uio.h>
14 #include <assert.h>
15 #if defined(__FreeBSD__)
16 # include <sys/param.h> /* FreeBSD sucks */
17 #endif
18
19 #include <sys/types.h>
20 #include <sys/stat.h>
21 #include <fcntl.h>
22 #include <unistd.h>
23 #include <stdio.h>
24
25 #include "php.h"
26 #include <php5/main/php_network.h>
27 #include <php5/ext/standard/file.h>
28 #include <ancillary.h>
29
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <fcntl.h>
33  
34 #define PHP_ANCILLARY_VERSION "1.0"
35 #define PHP_ANCILLARY_EXTNAME "ancillary"
36
37 /* log on stdout */
38 /* #define AO_STDLOG 1 */
39
40 /* log on /tmp/fd_recv.log */
41 /* #define AO_TMPLOG 1 */
42
43 extern zend_module_entry ancillary_module_entry;
44 #define phpext_ancillary_ptr &ancillary_module_entry
45
46 int
47 ancil_recv_fds_with_buffer(int sock, int *fds, unsigned n_fds, void *buffer)
48 {
49     struct msghdr msghdr;
50     char nothing;
51     struct iovec nothing_ptr;
52     struct cmsghdr *cmsg;
53     int i;
54
55     nothing_ptr.iov_base = &nothing;
56     nothing_ptr.iov_len = 1;
57     msghdr.msg_name = NULL;
58     msghdr.msg_namelen = 0;
59     msghdr.msg_iov = &nothing_ptr;
60     msghdr.msg_iovlen = 1;
61     msghdr.msg_flags = 0;
62     msghdr.msg_control = buffer;
63     msghdr.msg_controllen = sizeof(struct cmsghdr) + sizeof(int) * n_fds;
64     cmsg = CMSG_FIRSTHDR(&msghdr);
65     cmsg->cmsg_len = msghdr.msg_controllen;
66     cmsg->cmsg_level = SOL_SOCKET;
67     cmsg->cmsg_type = SCM_RIGHTS;
68     for(i = 0; i < n_fds; i++)
69         ((int *)CMSG_DATA(cmsg))[i] = -1;
70     
71     if(recvmsg(sock, &msghdr, 0) < 0)
72         return(-1);
73     for(i = 0; i < n_fds; i++)
74         fds[i] = ((int *)CMSG_DATA(cmsg))[i];
75     n_fds = (cmsg->cmsg_len - sizeof(struct cmsghdr)) / sizeof(int);
76     return(n_fds);
77 }
78
79 int
80 ancil_recv_fds_with_buffer_ext(int sock, int *fds, unsigned n_fds, void *buffer, char *headers, int hsize)
81 {
82     struct msghdr msghdr;
83     char nothing;
84     struct iovec nothing_ptr;
85     struct cmsghdr *cmsg;
86     int i, mop_len;
87
88     nothing_ptr.iov_base = &nothing;
89     nothing_ptr.iov_len = 1;
90     msghdr.msg_name = NULL;
91     msghdr.msg_namelen = 0;
92     msghdr.msg_iov = &nothing_ptr;
93     msghdr.msg_iovlen = 1;
94     msghdr.msg_flags = 0;
95     msghdr.msg_control = buffer;
96     msghdr.msg_controllen = sizeof(struct cmsghdr) + (sizeof(int) * n_fds) + hsize;
97     cmsg = CMSG_FIRSTHDR(&msghdr);
98     cmsg->cmsg_len = msghdr.msg_controllen;
99     cmsg->cmsg_level = SOL_SOCKET;
100     cmsg->cmsg_type = SCM_RIGHTS;
101     for(i = 0; i < n_fds; i++)
102         ((int *)CMSG_DATA(cmsg))[i] = -1;
103     if((mop_len = recvmsg(sock, &msghdr, 0)) < 0)
104         return(-1);
105
106 #ifdef AO_TMPLOG
107     {
108         int mop_fd;
109         char mop_bf[512];
110
111         mop_fd = open("/tmp/fd_recv.log", O_WRONLY | O_APPEND | O_CREAT);
112         sprintf(mop_bf, "LEN: [%d]\n", mop_len);
113         write(mop_fd, mop_bf, strlen(mop_bf));
114         close(mop_fd);
115
116     }
117 #endif
118     for(i = 0; i < n_fds; i++)
119         fds[i] = ((int *)CMSG_DATA(cmsg))[i];
120     // n_fds = (cmsg->cmsg_len - sizeof(struct cmsghdr)) / sizeof(int);
121     memcpy(headers, (char *)(CMSG_DATA(cmsg) + (sizeof(int) * n_fds)), hsize);
122
123     return(n_fds);
124 }
125
126 #ifndef SPARE_RECV_FDS
127 int
128 ancil_recv_fds(int sock, int *fd, unsigned n_fds)
129 {
130     ANCIL_FD_BUFFER(ANCIL_MAX_N_FDS) buffer;
131
132     assert(n_fds <= ANCIL_MAX_N_FDS);
133     return(ancil_recv_fds_with_buffer(sock, fd, n_fds, &buffer));
134 }
135 #endif /* SPARE_RECV_FDS */
136
137 #ifndef SPARE_RECV_FD
138 int
139 ancil_recv_fd(int sock, int *fd)
140 {
141     ANCIL_FD_BUFFER(1) buffer;
142
143     return(ancil_recv_fds_with_buffer(sock, fd, 1, &buffer) == 1 ? 0 : -1);
144 }
145 int
146 ancil_recv_fd_ext(int sock, int *fd, char *headers, int hsize)
147 {
148     ANCIL_FD_BUFFER(1) buffer;
149
150     return(ancil_recv_fds_with_buffer_ext(sock, fd, 1, &buffer, headers, hsize) == 1 ? 0 : -1);
151 }
152 #endif /* SPARE_RECV_FD */
153
154  
155 // declaration of a custom mop_function()
156 PHP_FUNCTION(ancillary_getstream);
157  
158 // list of custom PHP functions provided by this extension
159 // set {NULL, NULL, NULL} as the last record to mark the end of list
160 static
161 #if ZEND_MODULE_API_NO >= 20131226
162  zend_function_entry
163 #elif ZEND_MODULE_API_NO >= 20010901
164  function_entry
165 #endif
166  ancillary_getstream[] = {
167     PHP_FE(ancillary_getstream, NULL)
168     {NULL, NULL, NULL}
169 };
170  
171 // the following code creates an entry for the module and registers it with Zend.
172 zend_module_entry ancillary_module_entry = {
173 #if ZEND_MODULE_API_NO >= 20010901
174     STANDARD_MODULE_HEADER,
175 #endif
176     PHP_ANCILLARY_EXTNAME,
177     ancillary_getstream,
178     NULL, // name of the MINIT function or NULL if not applicable
179     NULL, // name of the MSHUTDOWN function or NULL if not applicable
180     NULL, // name of the RINIT function or NULL if not applicable
181     NULL, // name of the RSHUTDOWN function or NULL if not applicable
182     NULL, // name of the MINFO function or NULL if not applicable
183 #if ZEND_MODULE_API_NO >= 20010901
184     PHP_ANCILLARY_VERSION,
185 #endif
186     STANDARD_MODULE_PROPERTIES
187 };
188  
189 ZEND_GET_MODULE(ancillary)
190
191 #define CTRL_BUFF_MAX_SZ (8*1024)
192  
193 // starting from a unix socket receive a socket from an external process
194 PHP_FUNCTION(ancillary_getstream)
195 {
196     zval *zstream;
197     php_stream *stream;
198     int fd_in, fd_out[2], retrecv, curpos = 0;
199     char *headers;
200     zval *zheaders;
201     php_netstream_data_t *sock;
202
203     if ((headers = calloc(CTRL_BUFF_MAX_SZ, 1)) == NULL) {
204         return;
205     }
206
207     if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rz", &zstream, &zheaders)) {
208         free(headers);
209         return;
210     }
211     
212     php_stream_from_zval(stream, &zstream);
213
214     if (php_stream_cast(stream, PHP_STREAM_AS_FD, (void*)&fd_in, REPORT_ERRORS) == FAILURE) {
215         free(headers);
216         RETURN_FALSE;
217     }
218
219     if(ancil_recv_fds(fd_in, fd_out, 2) == 0) {
220         free(headers);
221         RETURN_FALSE;
222     }
223
224 #ifdef AO_STDLOG
225     {
226         char bf[2048];
227
228         sprintf(bf, "zero: [%d]    uno: [%d]\n", fd_out[0], fd_out[1]);
229         write(1, bf, strlen(bf));
230     }
231 #endif
232
233     while(1) {
234 #ifdef AO_STDLOG
235         write(1, "LOOP\n", 5);
236 #endif
237         retrecv = recv(fd_out[1], &(headers[curpos]), CTRL_BUFF_MAX_SZ - curpos - 1, 0);
238         if (retrecv < 0) {
239             free(headers);
240             RETURN_FALSE;
241         }
242         else if (retrecv == 0) {
243             break;
244         }
245         else {
246             char bf[1024];
247             curpos += retrecv;
248 #ifdef AO_STDLOG
249             sprintf(bf, "CURPOS: %d\n", curpos);
250             write(1, bf, strlen(bf));
251 #endif
252             if (curpos == CTRL_BUFF_MAX_SZ - 1) {
253                 free(headers);
254                 RETURN_FALSE;
255             }
256         }
257     }
258     shutdown(fd_out[1], SHUT_RDWR);
259     close(fd_out[1]);
260 #ifdef AO_STDLOG
261     write(1, "HEADERS[", 8);
262     write(1, headers, curpos);
263 #endif
264     headers[curpos] = '\0';
265 #ifdef AO_STDLOG
266     write(1, "]\n", 2);
267 #endif
268     ZVAL_STRING(zheaders, headers, 1);
269     free(headers);
270
271 #ifdef AO_TMPLOG
272     {
273         int fh;
274
275         if ((fh = open("/tmp/out_php-anc.txt", O_WRONLY | O_CREAT | O_APPEND)) > -1) {
276             write(fh, headers, curpos);
277             close(fh);
278         }
279     }
280 #endif
281
282     sock = pemalloc(sizeof(php_netstream_data_t), 0);
283     memset(sock, 0, sizeof(php_netstream_data_t));
284
285     sock->is_blocked = 1;
286     sock->timeout.tv_sec = FG(default_socket_timeout);
287     sock->timeout.tv_usec = 0;
288
289     /* we don't know the socket until we have determined if we are binding or
290      * connecting */
291     sock->socket = fd_out[0];
292
293     stream = php_stream_alloc_rel(&php_stream_socket_ops, sock, NULL, "r+");
294
295     if (stream == NULL) {
296         pefree(sock, 0);
297         return;
298     }
299     php_stream_to_zval(stream, return_value);
300 }
301