3 * Copyright 2015 gRPC authors.
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
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
25 #include "channel_credentials.h"
26 #include "call_credentials.h"
27 #include "server_credentials.h"
28 #include "completion_queue.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>
37 #ifdef GRPC_POSIX_FORK_ALLOW_PTHREAD_ATFORK
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[]
47 * Every user visible function must have an entry in grpc_functions[].
49 const zend_function_entry grpc_functions[] = {
50 PHP_FE_END /* Must be the last line in grpc_functions[] */
54 ZEND_DECLARE_MODULE_GLOBALS(grpc);
56 /* {{{ grpc_module_entry
58 zend_module_entry grpc_module_entry = {
59 STANDARD_MODULE_HEADER,
68 PHP_MODULE_GLOBALS(grpc),
72 STANDARD_MODULE_PROPERTIES_EX};
75 #ifdef COMPILE_DL_GRPC
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)
95 /* {{{ php_grpc_init_globals
97 /* Uncomment this function if you have INI entries
98 static void php_grpc_init_globals(zend_grpc_globals *grpc_globals)
100 grpc_globals->global_value = 0;
101 grpc_globals->global_string = NULL;
106 void create_new_channel(
107 wrapped_grpc_channel *channel,
109 grpc_channel_args args,
110 wrapped_grpc_channel_credentials *creds) {
112 channel->wrapper->wrapped = grpc_insecure_channel_create(target, &args,
115 channel->wrapper->wrapped =
116 grpc_secure_channel_create(creds->wrapped, target, &args, NULL);
120 void acquire_persistent_locks() {
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)
128 channel_persistent_le_t* le = rsrc->ptr;
130 gpr_mu_lock(&le->channel->mu);
131 PHP_GRPC_HASH_FOREACH_END()
134 void release_persistent_locks() {
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)
142 channel_persistent_le_t* le = rsrc->ptr;
144 gpr_mu_unlock(&le->channel->mu);
145 PHP_GRPC_HASH_FOREACH_END()
148 void destroy_grpc_channels() {
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)
156 channel_persistent_le_t* le = rsrc->ptr;
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()
165 void restart_channels() {
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)
173 channel_persistent_le_t* le = rsrc->ptr;
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,
180 gpr_mu_unlock(&channel->mu);
181 PHP_GRPC_HASH_FOREACH_END()
185 acquire_persistent_locks();
188 void postfork_child() {
191 // loop through persistent list and destroy all underlying grpc_channel objs
192 destroy_grpc_channels();
194 // clear completion queue
195 grpc_php_shutdown_completion_queue(TSRMLS_C);
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()",
207 grpc_php_init_completion_queue(TSRMLS_C);
209 // re-create grpc_channel and point wrapped to it
210 // unlock wrapped grpc channel mutex
214 void postfork_parent() {
215 release_persistent_locks();
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
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");
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));
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);
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));
258 static void custom_logger(gpr_log_func_args* args) {
261 const char* final_slash;
262 const char* display_file;
265 gpr_timespec now = gpr_now(GPR_CLOCK_REALTIME);
267 final_slash = strrchr(args->file, '/');
269 display_file = final_slash + 1;
271 display_file = args->file;
274 FILE *fp = fopen(GRPC_G(log_filename), "ab");
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);
283 gpr_asprintf(&final, "%-60s %s\n", prefix, args->message);
285 fprintf(fp, "%s", final);
291 /* {{{ PHP_MINIT_FUNCTION
293 PHP_MINIT_FUNCTION(grpc) {
294 REGISTER_INI_ENTRIES();
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);
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);
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);
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);
400 /* Register connectivity state constants */
401 REGISTER_LONG_CONSTANT("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",
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);
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);
428 /* {{{ PHP_MSHUTDOWN_FUNCTION
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;
448 /* {{{ PHP_MINFO_FUNCTION
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();
459 /* {{{ PHP_RINIT_FUNCTION
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);
468 register_fork_handlers();
469 grpc_php_init_completion_queue(TSRMLS_C);
470 GRPC_G(initialized) = 1;
476 /* {{{ PHP_GINIT_FUNCTION
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;
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.