Imported Upstream version 1.27.0
[platform/upstream/grpc.git] / src / php / ext / grpc / php_grpc.c
1 /*
2  *
3  * Copyright 2015 gRPC authors.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  */
18
19 #include "php_grpc.h"
20
21 #include "call.h"
22 #include "channel.h"
23 #include "server.h"
24 #include "timeval.h"
25 #include "channel_credentials.h"
26 #include "call_credentials.h"
27 #include "server_credentials.h"
28 #include "completion_queue.h"
29 #include <inttypes.h>
30 #include <grpc/support/alloc.h>
31 #include <grpc/support/log.h>
32 #include <grpc/support/string_util.h>
33 #include <grpc/support/time.h>
34 #include <ext/spl/spl_exceptions.h>
35 #include <zend_exceptions.h>
36
37 #ifdef GRPC_POSIX_FORK_ALLOW_PTHREAD_ATFORK
38 #include <pthread.h>
39 #endif
40
41 ZEND_DECLARE_MODULE_GLOBALS(grpc)
42 static PHP_GINIT_FUNCTION(grpc);
43 HashTable grpc_persistent_list;
44 HashTable grpc_target_upper_bound_map;
45 /* {{{ grpc_functions[]
46  *
47  * Every user visible function must have an entry in grpc_functions[].
48  */
49 const zend_function_entry grpc_functions[] = {
50     PHP_FE_END /* Must be the last line in grpc_functions[] */
51 };
52 /* }}} */
53
54 ZEND_DECLARE_MODULE_GLOBALS(grpc);
55
56 /* {{{ grpc_module_entry
57  */
58 zend_module_entry grpc_module_entry = {
59   STANDARD_MODULE_HEADER,
60   "grpc",
61   grpc_functions,
62   PHP_MINIT(grpc),
63   PHP_MSHUTDOWN(grpc),
64   PHP_RINIT(grpc),
65   NULL,
66   PHP_MINFO(grpc),
67   PHP_GRPC_VERSION,
68   PHP_MODULE_GLOBALS(grpc),
69   PHP_GINIT(grpc),
70   NULL,
71   NULL,
72   STANDARD_MODULE_PROPERTIES_EX};
73 /* }}} */
74
75 #ifdef COMPILE_DL_GRPC
76 ZEND_GET_MODULE(grpc)
77 #endif
78
79 /* {{{ PHP_INI
80  */
81    PHP_INI_BEGIN()
82    STD_PHP_INI_ENTRY("grpc.enable_fork_support", "0", PHP_INI_SYSTEM, OnUpdateBool,
83                      enable_fork_support, zend_grpc_globals, grpc_globals)
84    STD_PHP_INI_ENTRY("grpc.poll_strategy", NULL, PHP_INI_SYSTEM, OnUpdateString,
85                      poll_strategy, zend_grpc_globals, grpc_globals)
86    STD_PHP_INI_ENTRY("grpc.grpc_verbosity", NULL, PHP_INI_SYSTEM, OnUpdateString,
87                      grpc_verbosity, zend_grpc_globals, grpc_globals)
88    STD_PHP_INI_ENTRY("grpc.grpc_trace", NULL, PHP_INI_SYSTEM, OnUpdateString,
89                      grpc_trace, zend_grpc_globals, grpc_globals)
90    STD_PHP_INI_ENTRY("grpc.log_filename", NULL, PHP_INI_SYSTEM, OnUpdateString,
91                      log_filename, zend_grpc_globals, grpc_globals)
92    PHP_INI_END()
93 /* }}} */
94
95 /* {{{ php_grpc_init_globals
96  */
97 /* Uncomment this function if you have INI entries
98    static void php_grpc_init_globals(zend_grpc_globals *grpc_globals)
99    {
100      grpc_globals->global_value = 0;
101      grpc_globals->global_string = NULL;
102    }
103 */
104 /* }}} */
105
106 void create_new_channel(
107     wrapped_grpc_channel *channel,
108     char *target,
109     grpc_channel_args args,
110     wrapped_grpc_channel_credentials *creds) {
111   if (creds == NULL) {
112     channel->wrapper->wrapped = grpc_insecure_channel_create(target, &args,
113                                                              NULL);
114   } else {
115     channel->wrapper->wrapped =
116         grpc_secure_channel_create(creds->wrapped, target, &args, NULL);
117   }
118 }
119
120 void acquire_persistent_locks() {
121   zval *data;
122   PHP_GRPC_HASH_FOREACH_VAL_START(&grpc_persistent_list, data)
123     php_grpc_zend_resource *rsrc  =
124                 (php_grpc_zend_resource*) PHP_GRPC_HASH_VALPTR_TO_VAL(data)
125     if (rsrc == NULL) {
126       break;
127     }
128     channel_persistent_le_t* le = rsrc->ptr;
129
130     gpr_mu_lock(&le->channel->mu);
131   PHP_GRPC_HASH_FOREACH_END()
132 }
133
134 void release_persistent_locks() {
135   zval *data;
136   PHP_GRPC_HASH_FOREACH_VAL_START(&grpc_persistent_list, data)
137     php_grpc_zend_resource *rsrc  =
138                 (php_grpc_zend_resource*) PHP_GRPC_HASH_VALPTR_TO_VAL(data)
139     if (rsrc == NULL) {
140       break;
141     }
142     channel_persistent_le_t* le = rsrc->ptr;
143
144     gpr_mu_unlock(&le->channel->mu);
145   PHP_GRPC_HASH_FOREACH_END()
146 }
147
148 void destroy_grpc_channels() {
149   zval *data;
150   PHP_GRPC_HASH_FOREACH_VAL_START(&grpc_persistent_list, data)
151     php_grpc_zend_resource *rsrc  =
152                 (php_grpc_zend_resource*) PHP_GRPC_HASH_VALPTR_TO_VAL(data)
153     if (rsrc == NULL) {
154       break;
155     }
156     channel_persistent_le_t* le = rsrc->ptr;
157
158     wrapped_grpc_channel wrapped_channel;
159     wrapped_channel.wrapper = le->channel;
160     grpc_channel_wrapper *channel = wrapped_channel.wrapper;
161     grpc_channel_destroy(channel->wrapped);
162   PHP_GRPC_HASH_FOREACH_END()
163 }
164
165 void restart_channels() {
166   zval *data;
167   PHP_GRPC_HASH_FOREACH_VAL_START(&grpc_persistent_list, data)
168     php_grpc_zend_resource *rsrc  =
169                 (php_grpc_zend_resource*) PHP_GRPC_HASH_VALPTR_TO_VAL(data)
170     if (rsrc == NULL) {
171       break;
172     }
173     channel_persistent_le_t* le = rsrc->ptr;
174
175     wrapped_grpc_channel wrapped_channel;
176     wrapped_channel.wrapper = le->channel;
177     grpc_channel_wrapper *channel = wrapped_channel.wrapper;
178     create_new_channel(&wrapped_channel, channel->target, channel->args,
179                        channel->creds);
180     gpr_mu_unlock(&channel->mu);
181   PHP_GRPC_HASH_FOREACH_END()
182 }
183
184 void prefork() {
185   acquire_persistent_locks();
186 }
187
188 void postfork_child() {
189   TSRMLS_FETCH();
190
191   // loop through persistent list and destroy all underlying grpc_channel objs
192   destroy_grpc_channels();
193
194   // clear completion queue
195   grpc_php_shutdown_completion_queue(TSRMLS_C);
196
197   // clean-up grpc_core
198   grpc_shutdown_blocking();
199   if (grpc_is_initialized() > 0) {
200     zend_throw_exception(spl_ce_UnexpectedValueException,
201                          "Oops, failed to shutdown gRPC Core after fork()",
202                          1 TSRMLS_CC);
203   }
204
205   // restart grpc_core
206   grpc_init();
207   grpc_php_init_completion_queue(TSRMLS_C);
208
209   // re-create grpc_channel and point wrapped to it
210   // unlock wrapped grpc channel mutex
211   restart_channels();
212 }
213
214 void postfork_parent() {
215   release_persistent_locks();
216 }
217
218 void register_fork_handlers() {
219   if (getenv("GRPC_ENABLE_FORK_SUPPORT")) {
220 #ifdef GRPC_POSIX_FORK_ALLOW_PTHREAD_ATFORK
221     pthread_atfork(&prefork, &postfork_parent, &postfork_child);
222 #endif  // GRPC_POSIX_FORK_ALLOW_PTHREAD_ATFORK
223   }
224 }
225
226 void apply_ini_settings(TSRMLS_D) {
227   if (GRPC_G(enable_fork_support)) {
228     char *enable_str = malloc(sizeof("GRPC_ENABLE_FORK_SUPPORT=1"));
229     strcpy(enable_str, "GRPC_ENABLE_FORK_SUPPORT=1");
230     putenv(enable_str);
231   }
232
233   if (GRPC_G(poll_strategy)) {
234     char *poll_str = malloc(sizeof("GRPC_POLL_STRATEGY=") +
235                             strlen(GRPC_G(poll_strategy)));
236     strcpy(poll_str, "GRPC_POLL_STRATEGY=");
237     strcat(poll_str, GRPC_G(poll_strategy));
238     putenv(poll_str);
239   }
240
241   if (GRPC_G(grpc_verbosity)) {
242     char *verbosity_str = malloc(sizeof("GRPC_VERBOSITY=") +
243                                  strlen(GRPC_G(grpc_verbosity)));
244     strcpy(verbosity_str, "GRPC_VERBOSITY=");
245     strcat(verbosity_str, GRPC_G(grpc_verbosity));
246     putenv(verbosity_str);
247   }
248
249   if (GRPC_G(grpc_trace)) {
250     char *trace_str = malloc(sizeof("GRPC_TRACE=") +
251                              strlen(GRPC_G(grpc_trace)));
252     strcpy(trace_str, "GRPC_TRACE=");
253     strcat(trace_str, GRPC_G(grpc_trace));
254     putenv(trace_str);
255   }
256 }
257
258 static void custom_logger(gpr_log_func_args* args) {
259   TSRMLS_FETCH();
260
261   const char* final_slash;
262   const char* display_file;
263   char* prefix;
264   char* final;
265   gpr_timespec now = gpr_now(GPR_CLOCK_REALTIME);
266
267   final_slash = strrchr(args->file, '/');
268   if (final_slash) {
269     display_file = final_slash + 1;
270   } else {
271     display_file = args->file;
272   }
273
274   FILE *fp = fopen(GRPC_G(log_filename), "ab");
275   if (!fp) {
276     return;
277   }
278
279   gpr_asprintf(&prefix, "%s%" PRId64 ".%09" PRId32 " %s:%d]",
280                gpr_log_severity_string(args->severity), now.tv_sec,
281                now.tv_nsec, display_file, args->line);
282
283   gpr_asprintf(&final, "%-60s %s\n", prefix, args->message);
284
285   fprintf(fp, "%s", final);
286   fclose(fp);
287   gpr_free(prefix);
288   gpr_free(final);
289 }
290
291 /* {{{ PHP_MINIT_FUNCTION
292  */
293 PHP_MINIT_FUNCTION(grpc) {
294   REGISTER_INI_ENTRIES();
295
296   /* Register call error constants */
297   REGISTER_LONG_CONSTANT("Grpc\\CALL_OK", GRPC_CALL_OK,
298                          CONST_CS | CONST_PERSISTENT);
299   REGISTER_LONG_CONSTANT("Grpc\\CALL_ERROR", GRPC_CALL_ERROR,
300                          CONST_CS | CONST_PERSISTENT);
301   REGISTER_LONG_CONSTANT("Grpc\\CALL_ERROR_NOT_ON_SERVER",
302                          GRPC_CALL_ERROR_NOT_ON_SERVER,
303                          CONST_CS | CONST_PERSISTENT);
304   REGISTER_LONG_CONSTANT("Grpc\\CALL_ERROR_NOT_ON_CLIENT",
305                          GRPC_CALL_ERROR_NOT_ON_CLIENT,
306                          CONST_CS | CONST_PERSISTENT);
307   REGISTER_LONG_CONSTANT("Grpc\\CALL_ERROR_ALREADY_INVOKED",
308                          GRPC_CALL_ERROR_ALREADY_INVOKED,
309                          CONST_CS | CONST_PERSISTENT);
310   REGISTER_LONG_CONSTANT("Grpc\\CALL_ERROR_NOT_INVOKED",
311                          GRPC_CALL_ERROR_NOT_INVOKED,
312                          CONST_CS | CONST_PERSISTENT);
313   REGISTER_LONG_CONSTANT("Grpc\\CALL_ERROR_ALREADY_FINISHED",
314                          GRPC_CALL_ERROR_ALREADY_FINISHED,
315                          CONST_CS | CONST_PERSISTENT);
316   REGISTER_LONG_CONSTANT("Grpc\\CALL_ERROR_TOO_MANY_OPERATIONS",
317                          GRPC_CALL_ERROR_TOO_MANY_OPERATIONS,
318                          CONST_CS | CONST_PERSISTENT);
319   REGISTER_LONG_CONSTANT("Grpc\\CALL_ERROR_INVALID_FLAGS",
320                          GRPC_CALL_ERROR_INVALID_FLAGS,
321                          CONST_CS | CONST_PERSISTENT);
322
323   /* Register flag constants */
324   REGISTER_LONG_CONSTANT("Grpc\\WRITE_BUFFER_HINT", GRPC_WRITE_BUFFER_HINT,
325                          CONST_CS | CONST_PERSISTENT);
326   REGISTER_LONG_CONSTANT("Grpc\\WRITE_NO_COMPRESS", GRPC_WRITE_NO_COMPRESS,
327                          CONST_CS | CONST_PERSISTENT);
328
329   /* Register status constants */
330   REGISTER_LONG_CONSTANT("Grpc\\STATUS_OK", GRPC_STATUS_OK,
331                          CONST_CS | CONST_PERSISTENT);
332   REGISTER_LONG_CONSTANT("Grpc\\STATUS_CANCELLED", GRPC_STATUS_CANCELLED,
333                          CONST_CS | CONST_PERSISTENT);
334   REGISTER_LONG_CONSTANT("Grpc\\STATUS_UNKNOWN", GRPC_STATUS_UNKNOWN,
335                          CONST_CS | CONST_PERSISTENT);
336   REGISTER_LONG_CONSTANT("Grpc\\STATUS_INVALID_ARGUMENT",
337                          GRPC_STATUS_INVALID_ARGUMENT,
338                          CONST_CS | CONST_PERSISTENT);
339   REGISTER_LONG_CONSTANT("Grpc\\STATUS_DEADLINE_EXCEEDED",
340                          GRPC_STATUS_DEADLINE_EXCEEDED,
341                          CONST_CS | CONST_PERSISTENT);
342   REGISTER_LONG_CONSTANT("Grpc\\STATUS_NOT_FOUND", GRPC_STATUS_NOT_FOUND,
343                          CONST_CS | CONST_PERSISTENT);
344   REGISTER_LONG_CONSTANT("Grpc\\STATUS_ALREADY_EXISTS",
345                          GRPC_STATUS_ALREADY_EXISTS,
346                          CONST_CS | CONST_PERSISTENT);
347   REGISTER_LONG_CONSTANT("Grpc\\STATUS_PERMISSION_DENIED",
348                          GRPC_STATUS_PERMISSION_DENIED,
349                          CONST_CS | CONST_PERSISTENT);
350   REGISTER_LONG_CONSTANT("Grpc\\STATUS_UNAUTHENTICATED",
351                          GRPC_STATUS_UNAUTHENTICATED,
352                          CONST_CS | CONST_PERSISTENT);
353   REGISTER_LONG_CONSTANT("Grpc\\STATUS_RESOURCE_EXHAUSTED",
354                          GRPC_STATUS_RESOURCE_EXHAUSTED,
355                          CONST_CS | CONST_PERSISTENT);
356   REGISTER_LONG_CONSTANT("Grpc\\STATUS_FAILED_PRECONDITION",
357                          GRPC_STATUS_FAILED_PRECONDITION,
358                          CONST_CS | CONST_PERSISTENT);
359   REGISTER_LONG_CONSTANT("Grpc\\STATUS_ABORTED", GRPC_STATUS_ABORTED,
360                          CONST_CS | CONST_PERSISTENT);
361   REGISTER_LONG_CONSTANT("Grpc\\STATUS_OUT_OF_RANGE",
362                          GRPC_STATUS_OUT_OF_RANGE,
363                          CONST_CS | CONST_PERSISTENT);
364   REGISTER_LONG_CONSTANT("Grpc\\STATUS_UNIMPLEMENTED",
365                          GRPC_STATUS_UNIMPLEMENTED,
366                          CONST_CS | CONST_PERSISTENT);
367   REGISTER_LONG_CONSTANT("Grpc\\STATUS_INTERNAL", GRPC_STATUS_INTERNAL,
368                          CONST_CS | CONST_PERSISTENT);
369   REGISTER_LONG_CONSTANT("Grpc\\STATUS_UNAVAILABLE", GRPC_STATUS_UNAVAILABLE,
370                          CONST_CS | CONST_PERSISTENT);
371   REGISTER_LONG_CONSTANT("Grpc\\STATUS_DATA_LOSS", GRPC_STATUS_DATA_LOSS,
372                          CONST_CS | CONST_PERSISTENT);
373
374   /* Register op type constants */
375   REGISTER_LONG_CONSTANT("Grpc\\OP_SEND_INITIAL_METADATA",
376                          GRPC_OP_SEND_INITIAL_METADATA,
377                          CONST_CS | CONST_PERSISTENT);
378   REGISTER_LONG_CONSTANT("Grpc\\OP_SEND_MESSAGE",
379                          GRPC_OP_SEND_MESSAGE,
380                          CONST_CS | CONST_PERSISTENT);
381   REGISTER_LONG_CONSTANT("Grpc\\OP_SEND_CLOSE_FROM_CLIENT",
382                          GRPC_OP_SEND_CLOSE_FROM_CLIENT,
383                          CONST_CS | CONST_PERSISTENT);
384   REGISTER_LONG_CONSTANT("Grpc\\OP_SEND_STATUS_FROM_SERVER",
385                          GRPC_OP_SEND_STATUS_FROM_SERVER,
386                          CONST_CS | CONST_PERSISTENT);
387   REGISTER_LONG_CONSTANT("Grpc\\OP_RECV_INITIAL_METADATA",
388                          GRPC_OP_RECV_INITIAL_METADATA,
389                          CONST_CS | CONST_PERSISTENT);
390   REGISTER_LONG_CONSTANT("Grpc\\OP_RECV_MESSAGE",
391                          GRPC_OP_RECV_MESSAGE,
392                          CONST_CS | CONST_PERSISTENT);
393   REGISTER_LONG_CONSTANT("Grpc\\OP_RECV_STATUS_ON_CLIENT",
394                          GRPC_OP_RECV_STATUS_ON_CLIENT,
395                          CONST_CS | CONST_PERSISTENT);
396   REGISTER_LONG_CONSTANT("Grpc\\OP_RECV_CLOSE_ON_SERVER",
397                          GRPC_OP_RECV_CLOSE_ON_SERVER,
398                          CONST_CS | CONST_PERSISTENT);
399
400   /* Register connectivity state constants */
401   REGISTER_LONG_CONSTANT("Grpc\\CHANNEL_IDLE",
402                          GRPC_CHANNEL_IDLE,
403                          CONST_CS | CONST_PERSISTENT);
404   REGISTER_LONG_CONSTANT("Grpc\\CHANNEL_CONNECTING",
405                          GRPC_CHANNEL_CONNECTING,
406                          CONST_CS | CONST_PERSISTENT);
407   REGISTER_LONG_CONSTANT("Grpc\\CHANNEL_READY",
408                          GRPC_CHANNEL_READY,
409                          CONST_CS | CONST_PERSISTENT);
410   REGISTER_LONG_CONSTANT("Grpc\\CHANNEL_TRANSIENT_FAILURE",
411                          GRPC_CHANNEL_TRANSIENT_FAILURE,
412                          CONST_CS | CONST_PERSISTENT);
413   REGISTER_LONG_CONSTANT("Grpc\\CHANNEL_FATAL_FAILURE",
414                          GRPC_CHANNEL_SHUTDOWN,
415                          CONST_CS | CONST_PERSISTENT);
416
417   grpc_init_call(TSRMLS_C);
418   GRPC_STARTUP(channel);
419   grpc_init_server(TSRMLS_C);
420   grpc_init_timeval(TSRMLS_C);
421   grpc_init_channel_credentials(TSRMLS_C);
422   grpc_init_call_credentials(TSRMLS_C);
423   grpc_init_server_credentials(TSRMLS_C);
424   return SUCCESS;
425 }
426 /* }}} */
427
428 /* {{{ PHP_MSHUTDOWN_FUNCTION
429  */
430 PHP_MSHUTDOWN_FUNCTION(grpc) {
431   UNREGISTER_INI_ENTRIES();
432   // WARNING: This function IS being called by PHP when the extension
433   // is unloaded but the logs were somehow suppressed.
434   if (GRPC_G(initialized)) {
435     zend_hash_clean(&grpc_persistent_list);
436     zend_hash_destroy(&grpc_persistent_list);
437     zend_hash_clean(&grpc_target_upper_bound_map);
438     zend_hash_destroy(&grpc_target_upper_bound_map);
439     grpc_shutdown_timeval(TSRMLS_C);
440     grpc_php_shutdown_completion_queue(TSRMLS_C);
441     grpc_shutdown_blocking();
442     GRPC_G(initialized) = 0;
443   }
444   return SUCCESS;
445 }
446 /* }}} */
447
448 /* {{{ PHP_MINFO_FUNCTION
449  */
450 PHP_MINFO_FUNCTION(grpc) {
451   php_info_print_table_start();
452   php_info_print_table_row(2, "grpc support", "enabled");
453   php_info_print_table_row(2, "grpc module version", PHP_GRPC_VERSION);
454   php_info_print_table_end();
455   DISPLAY_INI_ENTRIES();
456 }
457 /* }}} */
458
459 /* {{{ PHP_RINIT_FUNCTION
460  */
461 PHP_RINIT_FUNCTION(grpc) {
462   if (!GRPC_G(initialized)) {
463     apply_ini_settings(TSRMLS_C);
464     if (GRPC_G(log_filename)) {
465       gpr_set_log_function(custom_logger);
466     }
467     grpc_init();
468     register_fork_handlers();
469     grpc_php_init_completion_queue(TSRMLS_C);
470     GRPC_G(initialized) = 1;
471   }
472   return SUCCESS;
473 }
474 /* }}} */
475
476 /* {{{ PHP_GINIT_FUNCTION
477  */
478 static PHP_GINIT_FUNCTION(grpc) {
479   grpc_globals->initialized = 0;
480   grpc_globals->enable_fork_support = 0;
481   grpc_globals->poll_strategy = NULL;
482   grpc_globals->grpc_verbosity = NULL;
483   grpc_globals->grpc_trace = NULL;
484   grpc_globals->log_filename = NULL;
485 }
486 /* }}} */
487
488 /* The previous line is meant for vim and emacs, so it can correctly fold and
489    unfold functions in source code. See the corresponding marks just before
490    function definition, where the functions purpose is also documented. Please
491    follow this convention for the convenience of others editing your code.
492 */