commit 9ab1638ec1f16c00b7921fe131429fce960ae90a Author: CismonX Date: Tue May 1 17:57:55 2018 +0800 archive diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..cd368d1 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.h linguist-language=C \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..dd65359 --- /dev/null +++ b/.gitignore @@ -0,0 +1,36 @@ +.deps +*.lo +*.la +.libs +acinclude.m4 +aclocal.m4 +autom4te.cache +build +config.guess +config.h +config.h.in +config.log +config.nice +config.status +config.sub +configure +configure.in +include +install-sh +libtool +ltmain.sh +Makefile +Makefile.fragments +Makefile.global +Makefile.objects +missing +mkinstalldirs +modules +run-tests.php +tests/*/*.diff +tests/*/*.out +tests/*/*.php +tests/*/*.exp +tests/*/*.log +tests/*/*.sh +.vscode/ diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..4d949fe --- /dev/null +++ b/.travis.yml @@ -0,0 +1,17 @@ +dist: bionic +group: edge + +language: php + +php: + - 7.1 + - 7.2 + - 7.3 + - 7.4 + +script: + - phpize + - ./configure --enable-ioctl-helper + - make + - make test + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..83a5457 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 CismonX + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..c6ae060 --- /dev/null +++ b/README.md @@ -0,0 +1,92 @@ +# ext-ioctl + +[![Travis-CI](https://travis-ci.com/CismonX/ext-ioctl.svg?branch=master)](https://travis-ci.com/CismonX/ext-ioctl) +[![MIT license](https://img.shields.io/badge/licence-MIT-blue.svg)](https://opensource.org/licenses/MIT) + +## 1. Introduction + +PHP binding for the [`ioctl()`](http://man7.org/linux/man-pages/man2/ioctl.2.html) system call. Supports PHP 7.1 and above. + +Using this extension is like playing with fire. Do not touch it unless you know **EXACTLY** what you are doing. + +## 2. Documentation + +### 2.1 Basic usage + +```PHP +/** + * Manipulates the underlying device parameters of special files. + * + * @param resource|int $fd + * @param int $request + * @param string $argp[optional] + * @param int $errno[optional] + * @return int|false + */ +function ioctl($fd, $request, $argp, &$errno) {} +``` + +* The `$fd` parameter can be an exact file descriptor (integer) or a `resource` holding it. +* The `$argp` parameter should be treated with care. + * If the underlying data type is a struct, memory alignment should be taken into consideration. + * If the `ioctl()` system call will write to `$argp`, you must pass a string buffer with enough size. + * PHP's `pack()` and `unpack()` functions can be very useful for this. +* If operation fails, `$errno` will be set, and the function will return `false`. + +See also: [examples](examples/). + +### 2.2 Helper functions + +In userland PHP it's not possible to allocate a string buffer without initialization, all we can do is something like `$buffer = str_repeat("\0", $length);`. + +This extension provides a helper function for better performance. + +```PHP +/** + * Allocate a string with given length without initialization. + * + * @param int $len + * @return string + */ +function str_alloc($len) {} +``` + +Sometimes, if the struct you are working with contains pointers, what you actually get is an unsigned integer, and there's nothing you can do with it. + +This extension provides two simple helper functions as a workaround. + +```PHP +/** + * Get the address of a string. + * + * @param string $str + * @return int + */ +function str2ptr($str) {} + +/** + * Copy data from the specified address into a string. + * + * @param int $ptr + * @param int $length + * @return string + */ +function ptr2str($ptr, $length) {} +``` + +* With `str2ptr()`, you can get the address of the initial byte of a `string` (the char array itself, not `zend_string`). +* If you receive a pointer, you can use `ptr2str()` to copy data from that address to a string buffer. + +Some `ioctl` operations requires a file descriptor in `$argp`. However, you cannot extract the underlying file descriptor from a `resource` in userland PHP. So we need another helper function. + +```PHP +/** + * Get underlying file descriptor of a resource. Returns -1 on failure. + * + * @param resource $res + * @return int + */ +function res2fd($res) {} +``` + +Note that the helper functions are not enabled by default. You should add `--enable-ioctl-helper` when executing configure script. diff --git a/config.m4 b/config.m4 new file mode 100644 index 0000000..cb0ebd2 --- /dev/null +++ b/config.m4 @@ -0,0 +1,12 @@ +PHP_ARG_ENABLE(ioctl, for ioctl support, +[ --enable-ioctl Enable ioctl support ]) + +PHP_ARG_ENABLE(ioctl-helper, for ioctl helper functions, +[ --enable-ioctl-helper Enable ioctl helper functions ], no, no) + +if test "$PHP_IOCTL" != "no"; then + PHP_NEW_EXTENSION(ioctl, src/ioctl.c, $ext_shared) + if test "$PHP_IOCTL_HELPER" != "no"; then + AC_DEFINE(IOCTL_ENABLE_HELPER_FUNCTIONS, 1, [ ]) + fi +fi diff --git a/examples/winsize.php b/examples/winsize.php new file mode 100644 index 0000000..fab6707 --- /dev/null +++ b/examples/winsize.php @@ -0,0 +1,81 @@ + +#include +#include + +#include "php_ioctl.h" + +#define ERRNO_SET(zval, err) \ + if (zval) { \ + ZVAL_DEREF(zval); \ + ZVAL_LONG(zval, err); \ + } + +PHP_FUNCTION(ioctl) +{ + zval *fd; + zend_long request; + zend_string *argp = NULL; + zval *err = NULL; + ZEND_PARSE_PARAMETERS_START(2, 4) + Z_PARAM_ZVAL(fd) + Z_PARAM_LONG(request) + Z_PARAM_OPTIONAL + Z_PARAM_STR(argp) + Z_PARAM_ZVAL(err) + ZEND_PARSE_PARAMETERS_END(); + php_stream *stream; + int fd_num; + if (Z_TYPE_P(fd) == IS_LONG) { + fd_num = Z_LVAL_P(fd); + } else if (UNEXPECTED(Z_TYPE_P(fd) != IS_RESOURCE)) { + ERRNO_SET(err, -1); + php_error_docref(NULL, E_WARNING, "Invalid fd. Resource or integer expected."); + RETURN_FALSE; + } else { + php_stream_from_zval(stream, fd); + struct { + FILE *file; + int fd; + } *stream_data = stream->abstract; + if (stream_data == NULL) { + ERRNO_SET(err, -2); + php_error_docref(NULL, E_WARNING, "Unexpected error. Failed to fetch stream data."); + RETURN_FALSE; + } + fd_num = stream_data->fd; + } + int result = ioctl(fd_num, (int)request, argp ? ZSTR_VAL(argp) : NULL); + if (result == -1) { + ERRNO_SET(err, errno); + RETURN_FALSE; + } + RETVAL_LONG(result); +} + +ZEND_BEGIN_ARG_INFO(ioctl_arginfo, 0) + ZEND_ARG_INFO(0, fd) + ZEND_ARG_TYPE_INFO(0, request, IS_LONG, 0) + ZEND_ARG_TYPE_INFO(0, argp, IS_STRING, 0) + ZEND_ARG_INFO(1, errno) +ZEND_END_ARG_INFO() + +#ifdef IOCTL_ENABLE_HELPER_FUNCTIONS + +PHP_FUNCTION(str_alloc) +{ + zend_long len; + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG(len); + ZEND_PARSE_PARAMETERS_END(); + if (len < 0) { + php_error_docref(NULL, E_WARNING, "Buffer length should be positive."); + RETURN_EMPTY_STRING(); + } + zend_string *buf = zend_string_alloc(len, 0); +#ifdef PHP_DEBUG + // Make the string nul-terminated to prevent warnings on debug builds of PHP. + ZSTR_VAL(buf)[len] = '\0'; +#endif + RETVAL_STR(buf); +} + +ZEND_BEGIN_ARG_INFO(str_alloc_arginfo, 0) + ZEND_ARG_TYPE_INFO(0, len, IS_LONG, 0) +ZEND_END_ARG_INFO() + +PHP_FUNCTION(str2ptr) +{ + zend_string *str; + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_STR(str); + ZEND_PARSE_PARAMETERS_END(); + RETVAL_LONG((zend_long)ZSTR_VAL(str)); +} + +ZEND_BEGIN_ARG_INFO(str2ptr_arginfo, 0) + ZEND_ARG_TYPE_INFO(0, str, IS_STRING, 0) +ZEND_END_ARG_INFO() + +PHP_FUNCTION(ptr2str) +{ + zend_long ptr; + zend_long length; + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_LONG(ptr) + Z_PARAM_LONG(length) + ZEND_PARSE_PARAMETERS_END(); + RETVAL_STR(zend_string_init((char*)ptr, length, 0)); +} + +ZEND_BEGIN_ARG_INFO(ptr2str_arginfo, 0) + ZEND_ARG_TYPE_INFO(0, ptr, IS_LONG, 0) + ZEND_ARG_TYPE_INFO(0, length, IS_LONG, 0) +ZEND_END_ARG_INFO() + +PHP_FUNCTION(res2fd) +{ + zval *res; + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(res) + ZEND_PARSE_PARAMETERS_END(); + php_stream* stream; + php_stream_from_zval(stream, res); + struct { + FILE *file; + int fd; + } *stream_data = stream->abstract; + if (stream_data) { + RETURN_LONG(stream_data->fd); + } + RETVAL_LONG(-1); +} + +ZEND_BEGIN_ARG_INFO(res2fd_arginfo, 0) + ZEND_ARG_TYPE_INFO(0, res, IS_RESOURCE, 0) +ZEND_END_ARG_INFO() + +#endif // IOCTL_ENABLE_HELPER_FUNCTIONS + +zend_function_entry ioctl_functions[] = { + PHP_FE(ioctl, ioctl_arginfo) +#ifdef IOCTL_ENABLE_HELPER_FUNCTIONS + PHP_FE(str_alloc, str_alloc_arginfo) + PHP_FE(str2ptr, str2ptr_arginfo) + PHP_FE(ptr2str, ptr2str_arginfo) + PHP_FE(res2fd, res2fd_arginfo) +#endif // IOCTL_ENABLE_HELPER_FUNCTIONS + PHP_FE_END +}; + +PHP_MINFO_FUNCTION(ioctl) +{ + php_info_print_table_start(); + php_info_print_table_header(2, "ioctl support", "enabled"); + php_info_print_table_end(); +} + +zend_module_entry ioctl_module_entry = { + STANDARD_MODULE_HEADER, + "ioctl", + ioctl_functions, + NULL, + NULL, + NULL, + NULL, + PHP_MINFO(ioctl), + PHP_IOCTL_VERSION, + STANDARD_MODULE_PROPERTIES +}; + +#ifdef COMPILE_DL_IOCTL +ZEND_GET_MODULE(ioctl) +#endif diff --git a/src/php_ioctl.h b/src/php_ioctl.h new file mode 100644 index 0000000..39c6208 --- /dev/null +++ b/src/php_ioctl.h @@ -0,0 +1,14 @@ +// +// ext-ioctl/php_ioctl.h +// +// @Author CismonX +// + +#ifndef PHP_IOCTL_H +#define PHP_IOCTL_H + +extern zend_module_entry ioctl_module_entry; + +#define PHP_IOCTL_VERSION "1.0.0" + +#endif // !PHP_IOCTL_H diff --git a/tests/00-loaded.phpt b/tests/00-loaded.phpt new file mode 100644 index 0000000..6d4da8a --- /dev/null +++ b/tests/00-loaded.phpt @@ -0,0 +1,8 @@ +--TEST-- +Test whether ext-ioctl is loaded. +--FILE-- + +--EXPECT-- diff --git a/tests/01-helpers.phpt b/tests/01-helpers.phpt new file mode 100644 index 0000000..764d931 --- /dev/null +++ b/tests/01-helpers.phpt @@ -0,0 +1,24 @@ +--TEST-- +Test whether helpers works normally. +--SKIPIF-- + +--FILE-- + +--EXPECT--