archive
This commit is contained in:
commit
9ab1638ec1
|
@ -0,0 +1 @@
|
|||
*.h linguist-language=C
|
|
@ -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/
|
|
@ -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
|
||||
|
|
@ -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.
|
|
@ -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.
|
|
@ -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
|
|
@ -0,0 +1,81 @@
|
|||
<?php
|
||||
/**
|
||||
* ext-ioctl/examples/winsize.php
|
||||
*
|
||||
* A simple example for using the ioctl() system call.
|
||||
* Get the current windows size of this TTY.
|
||||
* This script only works if you are using PHP-CLI on a TTY.
|
||||
*
|
||||
* @author CismonX
|
||||
*/
|
||||
|
||||
// Constants defined in sys/ioctl.h
|
||||
const TIOCGWINSZ = 0x5413;
|
||||
|
||||
// We need `pcntl_signal()` for this example.
|
||||
if (!extension_loaded('pcntl')) {
|
||||
echo 'To try this example, please install pcntl extension.', PHP_EOL;
|
||||
exit;
|
||||
}
|
||||
|
||||
// Get file descriptor of current TTY.
|
||||
$handle = fopen(rtrim(`tty`), 'r+');
|
||||
if ($handle === false) {
|
||||
echo 'To try this example, please run PHP-CLI on a TTY.', PHP_EOL;
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch current TTY window size.
|
||||
*
|
||||
* @param resource $handle
|
||||
* @return array|false
|
||||
*/
|
||||
function get_window_size($handle) {
|
||||
// See definition of struct winsize:
|
||||
//
|
||||
// struct winsize {
|
||||
// unsigned short ws_row;
|
||||
// unsigned short ws_col;
|
||||
// unsigned short ws_xpixel; /* unused */
|
||||
// unsigned short ws_ypixel; /* unused */
|
||||
// };
|
||||
//
|
||||
// Assuming we're on a 64-bit platform, an unsigned short usually contains 16 bits.
|
||||
// So we should allocate an 8-byte buffer.
|
||||
$buffer = str_alloc(8);
|
||||
|
||||
// Fetch current window size via ioctl().
|
||||
$result = ioctl($handle, TIOCGWINSZ, $buffer, $errno);
|
||||
if ($result === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Unpack the buffer into an array of four integers.
|
||||
return unpack('S4', $buffer);
|
||||
}
|
||||
|
||||
// Get current window size.
|
||||
$winsize = get_window_size($handle);
|
||||
if (!$winsize) {
|
||||
echo 'Failed to get window size. ', posix_strerror($errno), PHP_EOL;
|
||||
exit;
|
||||
}
|
||||
[, $height, $width] = $winsize;
|
||||
echo "Window height: $height, width: $width. Try resize window or press Ctrl+C to quit.", PHP_EOL;
|
||||
|
||||
// Enable asynchronous signal handling.
|
||||
pcntl_async_signals(true);
|
||||
|
||||
// Register signal handler which gets invoked when window size changes.
|
||||
pcntl_signal(SIGWINCH, function ($signo) use ($handle) {
|
||||
$winsize = get_window_size($handle);
|
||||
if ($winsize) {
|
||||
[, $height, $width] = $winsize;
|
||||
echo "SIGWINCH caught. Window height: $height, width: $width.", PHP_EOL;
|
||||
}
|
||||
});
|
||||
|
||||
while (1) {
|
||||
sleep(1);
|
||||
}
|
|
@ -0,0 +1,182 @@
|
|||
//
|
||||
// ext-ioctl/ioctl.c
|
||||
//
|
||||
// @Author CismonX
|
||||
//
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <php.h>
|
||||
#include <ext/standard/info.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#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
|
|
@ -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
|
|
@ -0,0 +1,8 @@
|
|||
--TEST--
|
||||
Test whether ext-ioctl is loaded.
|
||||
--FILE--
|
||||
<?php
|
||||
if (!extension_loaded('ioctl'))
|
||||
echo 'Extension ioctl is not loaded.', PHP_EOL;
|
||||
?>
|
||||
--EXPECT--
|
|
@ -0,0 +1,24 @@
|
|||
--TEST--
|
||||
Test whether helpers works normally.
|
||||
--SKIPIF--
|
||||
<?php
|
||||
if (!function_exists('res2fd'))
|
||||
echo 'skip helper functions are disabled';
|
||||
?>
|
||||
--FILE--
|
||||
<?php
|
||||
$buf = str_alloc(10);
|
||||
if (strlen($buf) != 10) {
|
||||
echo 'Test for `str_alloc()` failed.', PHP_EOL;
|
||||
}
|
||||
$str = 'hello';
|
||||
$str_addr = str2ptr($str);
|
||||
if ($str != ptr2str($str_addr, strlen($str))) {
|
||||
echo 'Test for `str2ptr()` and `ptr2str()` failed.', PHP_EOL;
|
||||
}
|
||||
$res = stream_socket_server('tcp://0.0.0.0:4567');
|
||||
if (res2fd($res) === false) {
|
||||
echo 'Test for `res2fd()` failed.', PHP_EOL;
|
||||
}
|
||||
?>
|
||||
--EXPECT--
|
Reference in New Issue