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