initial import

This commit is contained in:
udo
2022-01-21 09:10:38 +00:00
parent ad3d65cdac
commit c047927b18
171 changed files with 16540 additions and 1 deletions
+237
View File
@@ -0,0 +1,237 @@
#ifndef MYSQL_CLIENT_PLUGIN_INCLUDED
/* Copyright (c) 2010, 2021, Oracle and/or its affiliates.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License, version 2.0,
as published by the Free Software Foundation.
This program is also distributed with certain software (including
but not limited to OpenSSL) that is licensed under separate terms,
as designated in a particular file or component or in included license
documentation. The authors of MySQL hereby grant you an additional
permission to link the program and your derivative works with the
separately licensed software that they have included with MySQL.
Without limiting anything contained in the foregoing, this file,
which is part of C Driver for MySQL (Connector/C), is also subject to the
Universal FOSS Exception, version 1.0, a copy of which can be found at
http://oss.oracle.com/licenses/universal-foss-exception.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License, version 2.0, for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/**
@file include/mysql/client_plugin.h
MySQL Client Plugin API.
This file defines the API for plugins that work on the client side
*/
#define MYSQL_CLIENT_PLUGIN_INCLUDED
#ifndef MYSQL_ABI_CHECK
#include <stdarg.h>
#include <stdlib.h>
#endif
/*
On Windows, exports from DLL need to be declared.
Also, plugin needs to be declared as extern "C" because MSVC
unlike other compilers, uses C++ mangling for variables not only
for functions.
*/
#undef MYSQL_PLUGIN_EXPORT
#if defined(_MSC_VER)
#if defined(MYSQL_DYNAMIC_PLUGIN)
#ifdef __cplusplus
#define MYSQL_PLUGIN_EXPORT extern "C" __declspec(dllexport)
#else
#define MYSQL_PLUGIN_EXPORT __declspec(dllexport)
#endif
#else /* MYSQL_DYNAMIC_PLUGIN */
#ifdef __cplusplus
#define MYSQL_PLUGIN_EXPORT extern "C"
#else
#define MYSQL_PLUGIN_EXPORT
#endif
#endif /*MYSQL_DYNAMIC_PLUGIN */
#else /*_MSC_VER */
#if defined(MYSQL_DYNAMIC_PLUGIN)
#define MYSQL_PLUGIN_EXPORT MY_ATTRIBUTE((visibility("default")))
#else
#define MYSQL_PLUGIN_EXPORT
#endif
#endif
#ifdef __cplusplus
extern "C" {
#endif
/* known plugin types */
#define MYSQL_CLIENT_reserved1 0
#define MYSQL_CLIENT_reserved2 1
#define MYSQL_CLIENT_AUTHENTICATION_PLUGIN 2
#define MYSQL_CLIENT_TRACE_PLUGIN 3
#define MYSQL_CLIENT_AUTHENTICATION_PLUGIN_INTERFACE_VERSION 0x0200
#define MYSQL_CLIENT_TRACE_PLUGIN_INTERFACE_VERSION 0x0200
#define MYSQL_CLIENT_MAX_PLUGINS 4
#define MYSQL_CLIENT_PLUGIN_AUTHOR_ORACLE "Oracle Corporation"
#define mysql_declare_client_plugin(X) \
MYSQL_PLUGIN_EXPORT st_mysql_client_plugin_##X \
_mysql_client_plugin_declaration_ = { \
MYSQL_CLIENT_##X##_PLUGIN, \
MYSQL_CLIENT_##X##_PLUGIN_INTERFACE_VERSION,
#define mysql_end_client_plugin }
/* generic plugin header structure */
#define MYSQL_CLIENT_PLUGIN_HEADER \
int type; \
unsigned int interface_version; \
const char *name; \
const char *author; \
const char *desc; \
unsigned int version[3]; \
const char *license; \
void *mysql_api; \
int (*init)(char *, size_t, int, va_list); \
int (*deinit)(void); \
int (*options)(const char *option, const void *); \
int (*get_options)(const char *option, void *);
struct st_mysql_client_plugin {
MYSQL_CLIENT_PLUGIN_HEADER
};
struct MYSQL;
/******** authentication plugin specific declarations *********/
#include "plugin_auth_common.h"
struct auth_plugin_t {
MYSQL_CLIENT_PLUGIN_HEADER
int (*authenticate_user)(MYSQL_PLUGIN_VIO *vio, struct MYSQL *mysql);
enum net_async_status (*authenticate_user_nonblocking)(MYSQL_PLUGIN_VIO *vio,
struct MYSQL *mysql,
int *result);
};
// Needed for the mysql_declare_client_plugin() macro. Do not use elsewhere.
typedef struct auth_plugin_t st_mysql_client_plugin_AUTHENTICATION;
/******** using plugins ************/
/**
loads a plugin and initializes it
@param mysql MYSQL structure.
@param name a name of the plugin to load
@param type type of plugin that should be loaded, -1 to disable type check
@param argc number of arguments to pass to the plugin initialization
function
@param ... arguments for the plugin initialization function
@retval
a pointer to the loaded plugin, or NULL in case of a failure
*/
struct st_mysql_client_plugin *mysql_load_plugin(struct MYSQL *mysql,
const char *name, int type,
int argc, ...);
/**
loads a plugin and initializes it, taking va_list as an argument
This is the same as mysql_load_plugin, but take va_list instead of
a list of arguments.
@param mysql MYSQL structure.
@param name a name of the plugin to load
@param type type of plugin that should be loaded, -1 to disable type check
@param argc number of arguments to pass to the plugin initialization
function
@param args arguments for the plugin initialization function
@retval
a pointer to the loaded plugin, or NULL in case of a failure
*/
struct st_mysql_client_plugin *mysql_load_plugin_v(struct MYSQL *mysql,
const char *name, int type,
int argc, va_list args);
/**
finds an already loaded plugin by name, or loads it, if necessary
@param mysql MYSQL structure.
@param name a name of the plugin to load
@param type type of plugin that should be loaded
@retval
a pointer to the plugin, or NULL in case of a failure
*/
struct st_mysql_client_plugin *mysql_client_find_plugin(struct MYSQL *mysql,
const char *name,
int type);
/**
adds a plugin structure to the list of loaded plugins
This is useful if an application has the necessary functionality
(for example, a special load data handler) statically linked into
the application binary. It can use this function to register the plugin
directly, avoiding the need to factor it out into a shared object.
@param mysql MYSQL structure. It is only used for error reporting
@param plugin an st_mysql_client_plugin structure to register
@retval
a pointer to the plugin, or NULL in case of a failure
*/
struct st_mysql_client_plugin *mysql_client_register_plugin(
struct MYSQL *mysql, struct st_mysql_client_plugin *plugin);
/**
set plugin options
Can be used to set extra options and affect behavior for a plugin.
This function may be called multiple times to set several options
@param plugin an st_mysql_client_plugin structure
@param option a string which specifies the option to set
@param value value for the option.
@retval 0 on success, 1 in case of failure
**/
int mysql_plugin_options(struct st_mysql_client_plugin *plugin,
const char *option, const void *value);
/**
get plugin options
Can be used to get options from a plugin.
This function may be called multiple times to get several options
@param plugin an st_mysql_client_plugin structure
@param option a string which specifies the option to get
@param[out] value value for the option.
@retval 0 on success, 1 in case of failure
**/
int mysql_plugin_get_option(struct st_mysql_client_plugin *plugin,
const char *option, void *value);
#ifdef __cplusplus
}
#endif
#endif
+144
View File
@@ -0,0 +1,144 @@
#ifndef ERRMSG_INCLUDED
#define ERRMSG_INCLUDED
/* Copyright (c) 2000, 2021, Oracle and/or its affiliates.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License, version 2.0,
as published by the Free Software Foundation.
This program is also distributed with certain software (including
but not limited to OpenSSL) that is licensed under separate terms,
as designated in a particular file or component or in included license
documentation. The authors of MySQL hereby grant you an additional
permission to link the program and your derivative works with the
separately licensed software that they have included with MySQL.
Without limiting anything contained in the foregoing, this file,
which is part of C Driver for MySQL (Connector/C), is also subject to the
Universal FOSS Exception, version 1.0, a copy of which can be found at
http://oss.oracle.com/licenses/universal-foss-exception.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License, version 2.0, for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/**
@file include/errmsg.h
Error messages for MySQL clients.
These are constant and use the CR_ prefix.
<mysqlclient_ername.h> will contain auto-generated mappings
containing the symbolic name and the number from this file,
and the english error messages in libmysql/errmsg.c.
Dynamic error messages for the daemon are in share/language/errmsg.sys.
The server equivalent to <errmsg.h> is <mysqld_error.h>.
The server equivalent to <mysqlclient_ername.h> is <mysqld_ername.h>.
Note that the auth subsystem also uses codes with a CR_ prefix.
*/
void init_client_errs(void);
void finish_client_errs(void);
extern const char *client_errors[]; /* Error messages */
#define CR_MIN_ERROR 2000 /* For easier client code */
#define CR_MAX_ERROR 2999
#define CLIENT_ERRMAP 2 /* Errormap used by my_error() */
/* Do not add error numbers before CR_ERROR_FIRST. */
/* If necessary to add lower numbers, change CR_ERROR_FIRST accordingly. */
#define CR_ERROR_FIRST 2000 /*Copy first error nr.*/
#define CR_UNKNOWN_ERROR 2000
#define CR_SOCKET_CREATE_ERROR 2001
#define CR_CONNECTION_ERROR 2002
#define CR_CONN_HOST_ERROR 2003
#define CR_IPSOCK_ERROR 2004
#define CR_UNKNOWN_HOST 2005
#define CR_SERVER_GONE_ERROR 2006
#define CR_VERSION_ERROR 2007
#define CR_OUT_OF_MEMORY 2008
#define CR_WRONG_HOST_INFO 2009
#define CR_LOCALHOST_CONNECTION 2010
#define CR_TCP_CONNECTION 2011
#define CR_SERVER_HANDSHAKE_ERR 2012
#define CR_SERVER_LOST 2013
#define CR_COMMANDS_OUT_OF_SYNC 2014
#define CR_NAMEDPIPE_CONNECTION 2015
#define CR_NAMEDPIPEWAIT_ERROR 2016
#define CR_NAMEDPIPEOPEN_ERROR 2017
#define CR_NAMEDPIPESETSTATE_ERROR 2018
#define CR_CANT_READ_CHARSET 2019
#define CR_NET_PACKET_TOO_LARGE 2020
#define CR_EMBEDDED_CONNECTION 2021
#define CR_PROBE_SLAVE_STATUS 2022
#define CR_PROBE_SLAVE_HOSTS 2023
#define CR_PROBE_SLAVE_CONNECT 2024
#define CR_PROBE_MASTER_CONNECT 2025
#define CR_SSL_CONNECTION_ERROR 2026
#define CR_MALFORMED_PACKET 2027
#define CR_WRONG_LICENSE 2028
/* new 4.1 error codes */
#define CR_NULL_POINTER 2029
#define CR_NO_PREPARE_STMT 2030
#define CR_PARAMS_NOT_BOUND 2031
#define CR_DATA_TRUNCATED 2032
#define CR_NO_PARAMETERS_EXISTS 2033
#define CR_INVALID_PARAMETER_NO 2034
#define CR_INVALID_BUFFER_USE 2035
#define CR_UNSUPPORTED_PARAM_TYPE 2036
#define CR_SHARED_MEMORY_CONNECTION 2037
#define CR_SHARED_MEMORY_CONNECT_REQUEST_ERROR 2038
#define CR_SHARED_MEMORY_CONNECT_ANSWER_ERROR 2039
#define CR_SHARED_MEMORY_CONNECT_FILE_MAP_ERROR 2040
#define CR_SHARED_MEMORY_CONNECT_MAP_ERROR 2041
#define CR_SHARED_MEMORY_FILE_MAP_ERROR 2042
#define CR_SHARED_MEMORY_MAP_ERROR 2043
#define CR_SHARED_MEMORY_EVENT_ERROR 2044
#define CR_SHARED_MEMORY_CONNECT_ABANDONED_ERROR 2045
#define CR_SHARED_MEMORY_CONNECT_SET_ERROR 2046
#define CR_CONN_UNKNOW_PROTOCOL 2047
#define CR_INVALID_CONN_HANDLE 2048
#define CR_UNUSED_1 2049
#define CR_FETCH_CANCELED 2050
#define CR_NO_DATA 2051
#define CR_NO_STMT_METADATA 2052
#define CR_NO_RESULT_SET 2053
#define CR_NOT_IMPLEMENTED 2054
#define CR_SERVER_LOST_EXTENDED 2055
#define CR_STMT_CLOSED 2056
#define CR_NEW_STMT_METADATA 2057
#define CR_ALREADY_CONNECTED 2058
#define CR_AUTH_PLUGIN_CANNOT_LOAD 2059
#define CR_DUPLICATE_CONNECTION_ATTR 2060
#define CR_AUTH_PLUGIN_ERR 2061
#define CR_INSECURE_API_ERR 2062
#define CR_FILE_NAME_TOO_LONG 2063
#define CR_SSL_FIPS_MODE_ERR 2064
#define CR_DEPRECATED_COMPRESSION_NOT_SUPPORTED 2065
#define CR_COMPRESSION_WRONGLY_CONFIGURED 2066
#define CR_KERBEROS_USER_NOT_FOUND 2067
#define CR_LOAD_DATA_LOCAL_INFILE_REJECTED 2068
#define CR_LOAD_DATA_LOCAL_INFILE_REALPATH_FAIL 2069
#define CR_DNS_SRV_LOOKUP_FAILED 2070
#define CR_MANDATORY_TRACKER_NOT_FOUND 2071
#define CR_INVALID_FACTOR_NO 2072
#define CR_ERROR_LAST /*Copy last error nr:*/ 2072
/* Add error numbers before CR_ERROR_LAST and change it accordingly. */
/* Visual Studio requires '__inline' for C code */
static inline const char *ER_CLIENT(int client_errno) {
if (client_errno >= CR_ERROR_FIRST && client_errno <= CR_ERROR_LAST)
return client_errors[client_errno - CR_ERROR_FIRST];
return client_errors[CR_UNKNOWN_ERROR - CR_ERROR_FIRST];
}
#endif /* ERRMSG_INCLUDED */
+98
View File
@@ -0,0 +1,98 @@
/* Copyright (c) 2014, 2021, Oracle and/or its affiliates.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License, version 2.0,
as published by the Free Software Foundation.
This program is also distributed with certain software (including
but not limited to OpenSSL) that is licensed under separate terms,
as designated in a particular file or component or in included license
documentation. The authors of MySQL hereby grant you an additional
permission to link the program and your derivative works with the
separately licensed software that they have included with MySQL.
Without limiting anything contained in the foregoing, this file,
which is part of C Driver for MySQL (Connector/C), is also subject to the
Universal FOSS Exception, version 1.0, a copy of which can be found at
http://oss.oracle.com/licenses/universal-foss-exception.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License, version 2.0, for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/**
@file field_types.h
@brief This file contains the field type.
@note This file can be imported both from C and C++ code, so the
definitions have to be constructed to support this.
*/
#ifndef FIELD_TYPES_INCLUDED
#define FIELD_TYPES_INCLUDED
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/*
* Constants exported from this package.
*/
/**
Column types for MySQL
*/
enum enum_field_types
#if defined(__cplusplus) && __cplusplus > 201103L
// N2764: Forward enum declarations, added in C++11
: int
#endif /* __cplusplus */
{ MYSQL_TYPE_DECIMAL,
MYSQL_TYPE_TINY,
MYSQL_TYPE_SHORT,
MYSQL_TYPE_LONG,
MYSQL_TYPE_FLOAT,
MYSQL_TYPE_DOUBLE,
MYSQL_TYPE_NULL,
MYSQL_TYPE_TIMESTAMP,
MYSQL_TYPE_LONGLONG,
MYSQL_TYPE_INT24,
MYSQL_TYPE_DATE,
MYSQL_TYPE_TIME,
MYSQL_TYPE_DATETIME,
MYSQL_TYPE_YEAR,
MYSQL_TYPE_NEWDATE, /**< Internal to MySQL. Not used in protocol */
MYSQL_TYPE_VARCHAR,
MYSQL_TYPE_BIT,
MYSQL_TYPE_TIMESTAMP2,
MYSQL_TYPE_DATETIME2, /**< Internal to MySQL. Not used in protocol */
MYSQL_TYPE_TIME2, /**< Internal to MySQL. Not used in protocol */
MYSQL_TYPE_TYPED_ARRAY, /**< Used for replication only */
MYSQL_TYPE_INVALID = 243,
MYSQL_TYPE_BOOL = 244, /**< Currently just a placeholder */
MYSQL_TYPE_JSON = 245,
MYSQL_TYPE_NEWDECIMAL = 246,
MYSQL_TYPE_ENUM = 247,
MYSQL_TYPE_SET = 248,
MYSQL_TYPE_TINY_BLOB = 249,
MYSQL_TYPE_MEDIUM_BLOB = 250,
MYSQL_TYPE_LONG_BLOB = 251,
MYSQL_TYPE_BLOB = 252,
MYSQL_TYPE_VAR_STRING = 253,
MYSQL_TYPE_STRING = 254,
MYSQL_TYPE_GEOMETRY = 255 };
#ifdef __cplusplus
} // extern "C"
#else
typedef enum enum_field_types enum_field_types;
#endif /* __cplusplus */
#endif /* FIELD_TYPES_INCLUDED */
+103
View File
@@ -0,0 +1,103 @@
/* Copyright (c) 2015, 2021, Oracle and/or its affiliates.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License, version 2.0,
as published by the Free Software Foundation.
This program is also distributed with certain software (including
but not limited to OpenSSL) that is licensed under separate terms,
as designated in a particular file or component or in included license
documentation. The authors of MySQL hereby grant you an additional
permission to link the program and your derivative works with the
separately licensed software that they have included with MySQL.
Without limiting anything contained in the foregoing, this file,
which is part of C Driver for MySQL (Connector/C), is also subject to the
Universal FOSS Exception, version 1.0, a copy of which can be found at
http://oss.oracle.com/licenses/universal-foss-exception.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License, version 2.0, for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
#ifndef _mysql_command_h
#define _mysql_command_h
/**
@file include/my_command.h
*/
/**
@enum enum_server_command
@brief A list of all MySQL protocol commands.
These are the top level commands the server can receive
while it listens for a new command in ::dispatch_command
@par Warning
Add new commands to the end of this list, otherwise old
servers won't be able to handle them as 'unsupported'.
*/
enum enum_server_command {
/**
Currently refused by the server. See ::dispatch_command.
Also used internally to mark the start of a session.
*/
COM_SLEEP,
COM_QUIT, /**< See @ref page_protocol_com_quit */
COM_INIT_DB, /**< See @ref page_protocol_com_init_db */
COM_QUERY, /**< See @ref page_protocol_com_query */
COM_FIELD_LIST, /**< Deprecated. See @ref page_protocol_com_field_list */
COM_CREATE_DB, /**< Currently refused by the server. See ::dispatch_command */
COM_DROP_DB, /**< Currently refused by the server. See ::dispatch_command */
COM_REFRESH, /**< Deprecated. See @ref page_protocol_com_refresh */
COM_DEPRECATED_1, /**< Deprecated, used to be COM_SHUTDOWN */
COM_STATISTICS, /**< See @ref page_protocol_com_statistics */
COM_PROCESS_INFO, /**< Deprecated. See @ref page_protocol_com_process_info */
COM_CONNECT, /**< Currently refused by the server. */
COM_PROCESS_KILL, /**< Deprecated. See @ref page_protocol_com_process_kill */
COM_DEBUG, /**< See @ref page_protocol_com_debug */
COM_PING, /**< See @ref page_protocol_com_ping */
COM_TIME, /**< Currently refused by the server. */
COM_DELAYED_INSERT, /**< Functionality removed. */
COM_CHANGE_USER, /**< See @ref page_protocol_com_change_user */
COM_BINLOG_DUMP, /**< See @ref page_protocol_com_binlog_dump */
COM_TABLE_DUMP,
COM_CONNECT_OUT,
COM_REGISTER_SLAVE,
COM_STMT_PREPARE, /**< See @ref page_protocol_com_stmt_prepare */
COM_STMT_EXECUTE, /**< See @ref page_protocol_com_stmt_execute */
/** See @ref page_protocol_com_stmt_send_long_data */
COM_STMT_SEND_LONG_DATA,
COM_STMT_CLOSE, /**< See @ref page_protocol_com_stmt_close */
COM_STMT_RESET, /**< See @ref page_protocol_com_stmt_reset */
COM_SET_OPTION, /**< See @ref page_protocol_com_set_option */
COM_STMT_FETCH, /**< See @ref page_protocol_com_stmt_fetch */
/**
Currently refused by the server. See ::dispatch_command.
Also used internally to mark the session as a "daemon",
i.e. non-client THD. Currently the scheduler and the GTID
code does use this state.
These threads won't be killed by `KILL`
@sa Event_scheduler::start, ::init_thd, ::kill_one_thread,
::Find_thd_with_id
*/
COM_DAEMON,
COM_BINLOG_DUMP_GTID,
COM_RESET_CONNECTION, /**< See @ref page_protocol_com_reset_connection */
COM_CLONE,
COM_SUBSCRIBE_GROUP_REPLICATION_STREAM,
/* don't forget to update const char *command_name[] in sql_parse.cc */
/* Must be last */
COM_END /**< Not a real command. Refused. */
};
#endif /* _mysql_command_h */
+114
View File
@@ -0,0 +1,114 @@
/* Copyright (c) 2019, 2021, Oracle and/or its affiliates.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License, version 2.0,
as published by the Free Software Foundation.
This program is also distributed with certain software (including
but not limited to OpenSSL) that is licensed under separate terms,
as designated in a particular file or component or in included license
documentation. The authors of MySQL hereby grant you an additional
permission to link the program and your derivative works with the
separately licensed software that they have included with MySQL.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License, version 2.0, for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
#ifndef MY_COMPRESS_INCLUDED
#define MY_COMPRESS_INCLUDED
/* List of valid values for compression_algorithm */
enum enum_compression_algorithm {
MYSQL_UNCOMPRESSED = 1,
MYSQL_ZLIB,
MYSQL_ZSTD,
MYSQL_INVALID
};
/**
Compress context information. relating to zlib compression.
*/
typedef struct mysql_zlib_compress_context {
/**
Compression level to use in zlib compression.
*/
unsigned int compression_level;
} mysql_zlib_compress_context;
typedef struct ZSTD_CCtx_s ZSTD_CCtx;
typedef struct ZSTD_DCtx_s ZSTD_DCtx;
/**
Compress context information relating to zstd compression.
*/
typedef struct mysql_zstd_compress_context {
/**
Pointer to compressor context.
*/
ZSTD_CCtx *cctx;
/**
Pointer to decompressor context.
*/
ZSTD_DCtx *dctx;
/**
Compression level to use in zstd compression.
*/
unsigned int compression_level;
} mysql_zstd_compress_context;
/**
Compression context information.
It encapsulate the context information based on compression method and
presents a generic struct.
*/
typedef struct mysql_compress_context {
enum enum_compression_algorithm algorithm; ///< Compression algorithm name.
union {
mysql_zlib_compress_context zlib_ctx; ///< Context information of zlib.
mysql_zstd_compress_context zstd_ctx; ///< Context information of zstd.
} u;
} mysql_compress_context;
/**
Get default compression level corresponding to a given compression method.
@param algorithm Compression Method. Possible values are zlib or zstd.
@return an unsigned int representing default compression level.
6 is the default compression level for zlib and 3 is the
default compression level for zstd.
*/
unsigned int mysql_default_compression_level(
enum enum_compression_algorithm algorithm);
/**
Initialize a compress context object to be associated with a NET object.
@param cmp_ctx Pointer to compression context.
@param algorithm Compression algorithm.
@param compression_level Compression level corresponding to the compression
algorithm.
*/
void mysql_compress_context_init(mysql_compress_context *cmp_ctx,
enum enum_compression_algorithm algorithm,
unsigned int compression_level);
/**
Deinitialize the compression context allocated.
@param mysql_compress_ctx Pointer to Compression context.
*/
void mysql_compress_context_deinit(mysql_compress_context *mysql_compress_ctx);
#endif // MY_COMPRESS_INCLUDED
+57
View File
@@ -0,0 +1,57 @@
/* Copyright (c) 2000, 2021, Oracle and/or its affiliates.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License, version 2.0,
as published by the Free Software Foundation.
This program is also distributed with certain software (including
but not limited to OpenSSL) that is licensed under separate terms,
as designated in a particular file or component or in included license
documentation. The authors of MySQL hereby grant you an additional
permission to link the program and your derivative works with the
separately licensed software that they have included with MySQL.
Without limiting anything contained in the foregoing, this file,
which is part of C Driver for MySQL (Connector/C), is also subject to the
Universal FOSS Exception, version 1.0, a copy of which can be found at
http://oss.oracle.com/licenses/universal-foss-exception.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License, version 2.0, for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
#ifndef _list_h_
#define _list_h_
/**
@file include/my_list.h
*/
typedef struct LIST {
#if defined(__cplusplus) && __cplusplus >= 201103L
struct LIST *prev{nullptr}, *next{nullptr};
void *data{nullptr};
#else
struct LIST *prev, *next;
void *data;
#endif
} LIST;
typedef int (*list_walk_action)(void *, void *);
extern LIST *list_add(LIST *root, LIST *element);
extern LIST *list_delete(LIST *root, LIST *element);
extern LIST *list_cons(void *data, LIST *root);
extern LIST *list_reverse(LIST *root);
extern void list_free(LIST *root, unsigned int free_data);
extern unsigned int list_length(LIST *);
extern int list_walk(LIST *, list_walk_action action, unsigned char *argument);
#define list_rest(a) ((a)->next)
#endif
+803
View File
@@ -0,0 +1,803 @@
/* Copyright (c) 2000, 2021, Oracle and/or its affiliates.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License, version 2.0,
as published by the Free Software Foundation.
This program is also distributed with certain software (including
but not limited to OpenSSL) that is licensed under separate terms,
as designated in a particular file or component or in included license
documentation. The authors of MySQL hereby grant you an additional
permission to link the program and your derivative works with the
separately licensed software that they have included with MySQL.
Without limiting anything contained in the foregoing, this file,
which is part of C Driver for MySQL (Connector/C), is also subject to the
Universal FOSS Exception, version 1.0, a copy of which can be found at
http://oss.oracle.com/licenses/universal-foss-exception.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License, version 2.0, for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/**
@file include/mysql.h
This file defines the client API to MySQL and also the ABI of the
dynamically linked libmysqlclient.
The ABI should never be changed in a released product of MySQL,
thus you need to take great care when changing the file. In case
the file is changed so the ABI is broken, you must also update
the SHARED_LIB_MAJOR_VERSION in cmake/mysql_version.cmake
*/
#ifndef _mysql_h
#define _mysql_h
#ifndef MYSQL_ABI_CHECK
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <sys/types.h>
#endif
// Legacy definition for the benefit of old code. Use uint64_t in new code.
// If you get warnings from printf, use the PRIu64 macro, or, if you need
// compatibility with older versions of the client library, cast
// before printing.
typedef uint64_t my_ulonglong;
#ifndef my_socket_defined
#define my_socket_defined
#ifdef _WIN32
#include <windows.h>
#ifdef WIN32_LEAN_AND_MEAN
#include <winsock2.h>
#endif
#define my_socket SOCKET
#else
typedef int my_socket;
#endif /* _WIN32 */
#endif /* my_socket_defined */
// Small extra definition to avoid pulling in my_compiler.h in client code.
// IWYU pragma: no_include "my_compiler.h"
#ifndef MY_COMPILER_INCLUDED
#if !defined(_WIN32)
#define STDCALL
#else
#define STDCALL __stdcall
#endif
#endif /* MY_COMPILER_INCLUDED */
#include "field_types.h"
#include "my_list.h"
#include "mysql_com.h"
/* Include declarations of plug-in API */
#include "mysql/client_plugin.h" // IWYU pragma: keep
/*
The client should be able to know which version it is compiled against,
even if mysql.h doesn't use this information directly.
*/
#include "mysql_version.h" // IWYU pragma: keep
// MYSQL_TIME is part of our public API.
#include "mysql_time.h" // IWYU pragma: keep
// The error messages are part of our public API.
#include "errmsg.h" // IWYU pragma: keep
#ifdef __cplusplus
extern "C" {
#endif
extern unsigned int mysql_port;
extern char *mysql_unix_port;
#define CLIENT_NET_RETRY_COUNT 1 /* Retry count */
#define CLIENT_NET_READ_TIMEOUT 365 * 24 * 3600 /* Timeout on read */
#define CLIENT_NET_WRITE_TIMEOUT 365 * 24 * 3600 /* Timeout on write */
#define IS_PRI_KEY(n) ((n)&PRI_KEY_FLAG)
#define IS_NOT_NULL(n) ((n)&NOT_NULL_FLAG)
#define IS_BLOB(n) ((n)&BLOB_FLAG)
/**
Returns true if the value is a number which does not need quotes for
the sql_lex.cc parser to parse correctly.
*/
#define IS_NUM(t) \
(((t) <= MYSQL_TYPE_INT24 && (t) != MYSQL_TYPE_TIMESTAMP) || \
(t) == MYSQL_TYPE_YEAR || (t) == MYSQL_TYPE_NEWDECIMAL)
#define IS_LONGDATA(t) ((t) >= MYSQL_TYPE_TINY_BLOB && (t) <= MYSQL_TYPE_STRING)
typedef struct MYSQL_FIELD {
char *name; /* Name of column */
char *org_name; /* Original column name, if an alias */
char *table; /* Table of column if column was a field */
char *org_table; /* Org table name, if table was an alias */
char *db; /* Database for table */
char *catalog; /* Catalog for table */
char *def; /* Default value (set by mysql_list_fields) */
unsigned long length; /* Width of column (create length) */
unsigned long max_length; /* Max width for selected set */
unsigned int name_length;
unsigned int org_name_length;
unsigned int table_length;
unsigned int org_table_length;
unsigned int db_length;
unsigned int catalog_length;
unsigned int def_length;
unsigned int flags; /* Div flags */
unsigned int decimals; /* Number of decimals in field */
unsigned int charsetnr; /* Character set */
enum enum_field_types type; /* Type of field. See mysql_com.h for types */
void *extension;
} MYSQL_FIELD;
typedef char **MYSQL_ROW; /* return data as array of strings */
typedef unsigned int MYSQL_FIELD_OFFSET; /* offset to current field */
#define MYSQL_COUNT_ERROR (~(uint64_t)0)
/* backward compatibility define - to be removed eventually */
#define ER_WARN_DATA_TRUNCATED WARN_DATA_TRUNCATED
typedef struct MYSQL_ROWS {
struct MYSQL_ROWS *next; /* list of rows */
MYSQL_ROW data;
unsigned long length;
} MYSQL_ROWS;
typedef MYSQL_ROWS *MYSQL_ROW_OFFSET; /* offset to current row */
struct MEM_ROOT;
typedef struct MYSQL_DATA {
MYSQL_ROWS *data;
struct MEM_ROOT *alloc;
uint64_t rows;
unsigned int fields;
} MYSQL_DATA;
enum mysql_option {
MYSQL_OPT_CONNECT_TIMEOUT,
MYSQL_OPT_COMPRESS,
MYSQL_OPT_NAMED_PIPE,
MYSQL_INIT_COMMAND,
MYSQL_READ_DEFAULT_FILE,
MYSQL_READ_DEFAULT_GROUP,
MYSQL_SET_CHARSET_DIR,
MYSQL_SET_CHARSET_NAME,
MYSQL_OPT_LOCAL_INFILE,
MYSQL_OPT_PROTOCOL,
MYSQL_SHARED_MEMORY_BASE_NAME,
MYSQL_OPT_READ_TIMEOUT,
MYSQL_OPT_WRITE_TIMEOUT,
MYSQL_OPT_USE_RESULT,
MYSQL_REPORT_DATA_TRUNCATION,
MYSQL_OPT_RECONNECT,
MYSQL_PLUGIN_DIR,
MYSQL_DEFAULT_AUTH,
MYSQL_OPT_BIND,
MYSQL_OPT_SSL_KEY,
MYSQL_OPT_SSL_CERT,
MYSQL_OPT_SSL_CA,
MYSQL_OPT_SSL_CAPATH,
MYSQL_OPT_SSL_CIPHER,
MYSQL_OPT_SSL_CRL,
MYSQL_OPT_SSL_CRLPATH,
MYSQL_OPT_CONNECT_ATTR_RESET,
MYSQL_OPT_CONNECT_ATTR_ADD,
MYSQL_OPT_CONNECT_ATTR_DELETE,
MYSQL_SERVER_PUBLIC_KEY,
MYSQL_ENABLE_CLEARTEXT_PLUGIN,
MYSQL_OPT_CAN_HANDLE_EXPIRED_PASSWORDS,
MYSQL_OPT_MAX_ALLOWED_PACKET,
MYSQL_OPT_NET_BUFFER_LENGTH,
MYSQL_OPT_TLS_VERSION,
MYSQL_OPT_SSL_MODE,
MYSQL_OPT_GET_SERVER_PUBLIC_KEY,
MYSQL_OPT_RETRY_COUNT,
MYSQL_OPT_OPTIONAL_RESULTSET_METADATA,
MYSQL_OPT_SSL_FIPS_MODE,
MYSQL_OPT_TLS_CIPHERSUITES,
MYSQL_OPT_COMPRESSION_ALGORITHMS,
MYSQL_OPT_ZSTD_COMPRESSION_LEVEL,
MYSQL_OPT_LOAD_DATA_LOCAL_DIR,
MYSQL_OPT_USER_PASSWORD,
};
/**
@todo remove the "extension", move st_mysql_options completely
out of mysql.h
*/
struct st_mysql_options_extention;
struct st_mysql_options {
unsigned int connect_timeout, read_timeout, write_timeout;
unsigned int port, protocol;
unsigned long client_flag;
char *host, *user, *password, *unix_socket, *db;
struct Init_commands_array *init_commands;
char *my_cnf_file, *my_cnf_group, *charset_dir, *charset_name;
char *ssl_key; /* PEM key file */
char *ssl_cert; /* PEM cert file */
char *ssl_ca; /* PEM CA file */
char *ssl_capath; /* PEM directory of CA-s? */
char *ssl_cipher; /* cipher to use */
char *shared_memory_base_name;
unsigned long max_allowed_packet;
bool compress, named_pipe;
/**
The local address to bind when connecting to remote server.
*/
char *bind_address;
/* 0 - never report, 1 - always report (default) */
bool report_data_truncation;
/* function pointers for local infile support */
int (*local_infile_init)(void **, const char *, void *);
int (*local_infile_read)(void *, char *, unsigned int);
void (*local_infile_end)(void *);
int (*local_infile_error)(void *, char *, unsigned int);
void *local_infile_userdata;
struct st_mysql_options_extention *extension;
};
enum mysql_status {
MYSQL_STATUS_READY,
MYSQL_STATUS_GET_RESULT,
MYSQL_STATUS_USE_RESULT,
MYSQL_STATUS_STATEMENT_GET_RESULT
};
enum mysql_protocol_type {
MYSQL_PROTOCOL_DEFAULT,
MYSQL_PROTOCOL_TCP,
MYSQL_PROTOCOL_SOCKET,
MYSQL_PROTOCOL_PIPE,
MYSQL_PROTOCOL_MEMORY
};
enum mysql_ssl_mode {
SSL_MODE_DISABLED = 1,
SSL_MODE_PREFERRED,
SSL_MODE_REQUIRED,
SSL_MODE_VERIFY_CA,
SSL_MODE_VERIFY_IDENTITY
};
enum mysql_ssl_fips_mode {
SSL_FIPS_MODE_OFF = 0,
SSL_FIPS_MODE_ON = 1,
SSL_FIPS_MODE_STRICT
};
typedef struct character_set {
unsigned int number; /* character set number */
unsigned int state; /* character set state */
const char *csname; /* collation name */
const char *name; /* character set name */
const char *comment; /* comment */
const char *dir; /* character set directory */
unsigned int mbminlen; /* min. length for multibyte strings */
unsigned int mbmaxlen; /* max. length for multibyte strings */
} MY_CHARSET_INFO;
struct MYSQL_METHODS;
struct MYSQL_STMT;
typedef struct MYSQL {
NET net; /* Communication parameters */
unsigned char *connector_fd; /* ConnectorFd for SSL */
char *host, *user, *passwd, *unix_socket, *server_version, *host_info;
char *info, *db;
struct CHARSET_INFO *charset;
MYSQL_FIELD *fields;
struct MEM_ROOT *field_alloc;
uint64_t affected_rows;
uint64_t insert_id; /* id if insert on table with NEXTNR */
uint64_t extra_info; /* Not used */
unsigned long thread_id; /* Id for connection in server */
unsigned long packet_length;
unsigned int port;
unsigned long client_flag, server_capabilities;
unsigned int protocol_version;
unsigned int field_count;
unsigned int server_status;
unsigned int server_language;
unsigned int warning_count;
struct st_mysql_options options;
enum mysql_status status;
enum enum_resultset_metadata resultset_metadata;
bool free_me; /* If free in mysql_close */
bool reconnect; /* set to 1 if automatic reconnect */
/* session-wide random string */
char scramble[SCRAMBLE_LENGTH + 1];
LIST *stmts; /* list of all statements */
const struct MYSQL_METHODS *methods;
void *thd;
/*
Points to boolean flag in MYSQL_RES or MYSQL_STMT. We set this flag
from mysql_stmt_close if close had to cancel result set of this object.
*/
bool *unbuffered_fetch_owner;
void *extension;
} MYSQL;
typedef struct MYSQL_RES {
uint64_t row_count;
MYSQL_FIELD *fields;
struct MYSQL_DATA *data;
MYSQL_ROWS *data_cursor;
unsigned long *lengths; /* column lengths of current row */
MYSQL *handle; /* for unbuffered reads */
const struct MYSQL_METHODS *methods;
MYSQL_ROW row; /* If unbuffered read */
MYSQL_ROW current_row; /* buffer to current row */
struct MEM_ROOT *field_alloc;
unsigned int field_count, current_field;
bool eof; /* Used by mysql_fetch_row */
/* mysql_stmt_close() had to cancel this result */
bool unbuffered_fetch_cancelled;
enum enum_resultset_metadata metadata;
void *extension;
} MYSQL_RES;
/**
Flag to indicate that COM_BINLOG_DUMP_GTID should
be used rather than COM_BINLOG_DUMP in the @sa mysql_binlog_open().
*/
#define MYSQL_RPL_GTID (1 << 16)
/**
Skip HEARBEAT events in the @sa mysql_binlog_fetch().
*/
#define MYSQL_RPL_SKIP_HEARTBEAT (1 << 17)
/**
Struct for information about a replication stream.
@sa mysql_binlog_open()
@sa mysql_binlog_fetch()
@sa mysql_binlog_close()
*/
typedef struct MYSQL_RPL {
size_t file_name_length; /** Length of the 'file_name' or 0 */
const char *file_name; /** Filename of the binary log to read */
uint64_t start_position; /** Position in the binary log to */
/* start reading from */
unsigned int server_id; /** Server ID to use when identifying */
/* with the master */
unsigned int flags; /** Flags, e.g. MYSQL_RPL_GTID */
/** Size of gtid set data */
size_t gtid_set_encoded_size;
/** Callback function which is called */
/* from @sa mysql_binlog_open() to */
/* fill command packet gtid set */
void (*fix_gtid_set)(struct MYSQL_RPL *rpl, unsigned char *packet_gtid_set);
void *gtid_set_arg; /** GTID set data or an argument for */
/* fix_gtid_set() callback function */
unsigned long size; /** Size of the packet returned by */
/* mysql_binlog_fetch() */
const unsigned char *buffer; /** Pointer to returned data */
} MYSQL_RPL;
/*
Set up and bring down the server; to ensure that applications will
work when linked against either the standard client library or the
embedded server library, these functions should be called.
*/
int STDCALL mysql_server_init(int argc, char **argv, char **groups);
void STDCALL mysql_server_end(void);
/*
mysql_server_init/end need to be called when using libmysqld or
libmysqlclient (exactly, mysql_server_init() is called by mysql_init() so
you don't need to call it explicitely; but you need to call
mysql_server_end() to free memory). The names are a bit misleading
(mysql_SERVER* to be used when using libmysqlCLIENT). So we add more general
names which suit well whether you're using libmysqld or libmysqlclient. We
intend to promote these aliases over the mysql_server* ones.
*/
#define mysql_library_init mysql_server_init
#define mysql_library_end mysql_server_end
/*
Set up and bring down a thread; these function should be called
for each thread in an application which opens at least one MySQL
connection. All uses of the connection(s) should be between these
function calls.
*/
bool STDCALL mysql_thread_init(void);
void STDCALL mysql_thread_end(void);
/*
Functions to get information from the MYSQL and MYSQL_RES structures
Should definitely be used if one uses shared libraries.
*/
uint64_t STDCALL mysql_num_rows(MYSQL_RES *res);
unsigned int STDCALL mysql_num_fields(MYSQL_RES *res);
bool STDCALL mysql_eof(MYSQL_RES *res);
MYSQL_FIELD *STDCALL mysql_fetch_field_direct(MYSQL_RES *res,
unsigned int fieldnr);
MYSQL_FIELD *STDCALL mysql_fetch_fields(MYSQL_RES *res);
MYSQL_ROW_OFFSET STDCALL mysql_row_tell(MYSQL_RES *res);
MYSQL_FIELD_OFFSET STDCALL mysql_field_tell(MYSQL_RES *res);
enum enum_resultset_metadata STDCALL mysql_result_metadata(MYSQL_RES *result);
unsigned int STDCALL mysql_field_count(MYSQL *mysql);
uint64_t STDCALL mysql_affected_rows(MYSQL *mysql);
uint64_t STDCALL mysql_insert_id(MYSQL *mysql);
unsigned int STDCALL mysql_errno(MYSQL *mysql);
const char *STDCALL mysql_error(MYSQL *mysql);
const char *STDCALL mysql_sqlstate(MYSQL *mysql);
unsigned int STDCALL mysql_warning_count(MYSQL *mysql);
const char *STDCALL mysql_info(MYSQL *mysql);
unsigned long STDCALL mysql_thread_id(MYSQL *mysql);
const char *STDCALL mysql_character_set_name(MYSQL *mysql);
int STDCALL mysql_set_character_set(MYSQL *mysql, const char *csname);
MYSQL *STDCALL mysql_init(MYSQL *mysql);
bool STDCALL mysql_ssl_set(MYSQL *mysql, const char *key, const char *cert,
const char *ca, const char *capath,
const char *cipher);
const char *STDCALL mysql_get_ssl_cipher(MYSQL *mysql);
bool STDCALL mysql_change_user(MYSQL *mysql, const char *user,
const char *passwd, const char *db);
MYSQL *STDCALL mysql_real_connect(MYSQL *mysql, const char *host,
const char *user, const char *passwd,
const char *db, unsigned int port,
const char *unix_socket,
unsigned long clientflag);
int STDCALL mysql_select_db(MYSQL *mysql, const char *db);
int STDCALL mysql_query(MYSQL *mysql, const char *q);
int STDCALL mysql_send_query(MYSQL *mysql, const char *q, unsigned long length);
int STDCALL mysql_real_query(MYSQL *mysql, const char *q, unsigned long length);
MYSQL_RES *STDCALL mysql_store_result(MYSQL *mysql);
MYSQL_RES *STDCALL mysql_use_result(MYSQL *mysql);
enum net_async_status STDCALL mysql_real_connect_nonblocking(
MYSQL *mysql, const char *host, const char *user, const char *passwd,
const char *db, unsigned int port, const char *unix_socket,
unsigned long clientflag);
enum net_async_status STDCALL mysql_send_query_nonblocking(
MYSQL *mysql, const char *query, unsigned long length);
enum net_async_status STDCALL mysql_real_query_nonblocking(
MYSQL *mysql, const char *query, unsigned long length);
enum net_async_status STDCALL
mysql_store_result_nonblocking(MYSQL *mysql, MYSQL_RES **result);
enum net_async_status STDCALL mysql_next_result_nonblocking(MYSQL *mysql);
enum net_async_status STDCALL mysql_select_db_nonblocking(MYSQL *mysql,
const char *db,
bool *error);
void STDCALL mysql_get_character_set_info(MYSQL *mysql,
MY_CHARSET_INFO *charset);
int STDCALL mysql_session_track_get_first(MYSQL *mysql,
enum enum_session_state_type type,
const char **data, size_t *length);
int STDCALL mysql_session_track_get_next(MYSQL *mysql,
enum enum_session_state_type type,
const char **data, size_t *length);
/* local infile support */
#define LOCAL_INFILE_ERROR_LEN 512
void mysql_set_local_infile_handler(
MYSQL *mysql, int (*local_infile_init)(void **, const char *, void *),
int (*local_infile_read)(void *, char *, unsigned int),
void (*local_infile_end)(void *),
int (*local_infile_error)(void *, char *, unsigned int), void *);
void mysql_set_local_infile_default(MYSQL *mysql);
int STDCALL mysql_shutdown(MYSQL *mysql,
enum mysql_enum_shutdown_level shutdown_level);
int STDCALL mysql_dump_debug_info(MYSQL *mysql);
int STDCALL mysql_refresh(MYSQL *mysql, unsigned int refresh_options);
int STDCALL mysql_kill(MYSQL *mysql, unsigned long pid);
int STDCALL mysql_set_server_option(MYSQL *mysql,
enum enum_mysql_set_option option);
int STDCALL mysql_ping(MYSQL *mysql);
const char *STDCALL mysql_stat(MYSQL *mysql);
const char *STDCALL mysql_get_server_info(MYSQL *mysql);
const char *STDCALL mysql_get_client_info(void);
unsigned long STDCALL mysql_get_client_version(void);
const char *STDCALL mysql_get_host_info(MYSQL *mysql);
unsigned long STDCALL mysql_get_server_version(MYSQL *mysql);
unsigned int STDCALL mysql_get_proto_info(MYSQL *mysql);
MYSQL_RES *STDCALL mysql_list_dbs(MYSQL *mysql, const char *wild);
MYSQL_RES *STDCALL mysql_list_tables(MYSQL *mysql, const char *wild);
MYSQL_RES *STDCALL mysql_list_processes(MYSQL *mysql);
int STDCALL mysql_options(MYSQL *mysql, enum mysql_option option,
const void *arg);
int STDCALL mysql_options4(MYSQL *mysql, enum mysql_option option,
const void *arg1, const void *arg2);
int STDCALL mysql_get_option(MYSQL *mysql, enum mysql_option option,
const void *arg);
void STDCALL mysql_free_result(MYSQL_RES *result);
enum net_async_status STDCALL mysql_free_result_nonblocking(MYSQL_RES *result);
void STDCALL mysql_data_seek(MYSQL_RES *result, uint64_t offset);
MYSQL_ROW_OFFSET STDCALL mysql_row_seek(MYSQL_RES *result,
MYSQL_ROW_OFFSET offset);
MYSQL_FIELD_OFFSET STDCALL mysql_field_seek(MYSQL_RES *result,
MYSQL_FIELD_OFFSET offset);
MYSQL_ROW STDCALL mysql_fetch_row(MYSQL_RES *result);
enum net_async_status STDCALL mysql_fetch_row_nonblocking(MYSQL_RES *res,
MYSQL_ROW *row);
unsigned long *STDCALL mysql_fetch_lengths(MYSQL_RES *result);
MYSQL_FIELD *STDCALL mysql_fetch_field(MYSQL_RES *result);
MYSQL_RES *STDCALL mysql_list_fields(MYSQL *mysql, const char *table,
const char *wild);
unsigned long STDCALL mysql_escape_string(char *to, const char *from,
unsigned long from_length);
unsigned long STDCALL mysql_hex_string(char *to, const char *from,
unsigned long from_length);
unsigned long STDCALL mysql_real_escape_string(MYSQL *mysql, char *to,
const char *from,
unsigned long length);
unsigned long STDCALL mysql_real_escape_string_quote(MYSQL *mysql, char *to,
const char *from,
unsigned long length,
char quote);
void STDCALL mysql_debug(const char *debug);
void STDCALL myodbc_remove_escape(MYSQL *mysql, char *name);
unsigned int STDCALL mysql_thread_safe(void);
bool STDCALL mysql_read_query_result(MYSQL *mysql);
int STDCALL mysql_reset_connection(MYSQL *mysql);
int STDCALL mysql_binlog_open(MYSQL *mysql, MYSQL_RPL *rpl);
int STDCALL mysql_binlog_fetch(MYSQL *mysql, MYSQL_RPL *rpl);
void STDCALL mysql_binlog_close(MYSQL *mysql, MYSQL_RPL *rpl);
/*
The following definitions are added for the enhanced
client-server protocol
*/
/* statement state */
enum enum_mysql_stmt_state {
MYSQL_STMT_INIT_DONE = 1,
MYSQL_STMT_PREPARE_DONE,
MYSQL_STMT_EXECUTE_DONE,
MYSQL_STMT_FETCH_DONE
};
/*
This structure is used to define bind information, and
internally by the client library.
Public members with their descriptions are listed below
(conventionally `On input' refers to the binds given to
mysql_stmt_bind_param, `On output' refers to the binds given
to mysql_stmt_bind_result):
buffer_type - One of the MYSQL_* types, used to describe
the host language type of buffer.
On output: if column type is different from
buffer_type, column value is automatically converted
to buffer_type before it is stored in the buffer.
buffer - On input: points to the buffer with input data.
On output: points to the buffer capable to store
output data.
The type of memory pointed by buffer must correspond
to buffer_type. See the correspondence table in
the comment to mysql_stmt_bind_param.
The two above members are mandatory for any kind of bind.
buffer_length - the length of the buffer. You don't have to set
it for any fixed length buffer: float, double,
int, etc. It must be set however for variable-length
types, such as BLOBs or STRINGs.
length - On input: in case when lengths of input values
are different for each execute, you can set this to
point at a variable containining value length. This
way the value length can be different in each execute.
If length is not NULL, buffer_length is not used.
Note, length can even point at buffer_length if
you keep bind structures around while fetching:
this way you can change buffer_length before
each execution, everything will work ok.
On output: if length is set, mysql_stmt_fetch will
write column length into it.
is_null - On input: points to a boolean variable that should
be set to TRUE for NULL values.
This member is useful only if your data may be
NULL in some but not all cases.
If your data is never NULL, is_null should be set to 0.
If your data is always NULL, set buffer_type
to MYSQL_TYPE_NULL, and is_null will not be used.
is_unsigned - On input: used to signify that values provided for one
of numeric types are unsigned.
On output describes signedness of the output buffer.
If, taking into account is_unsigned flag, column data
is out of range of the output buffer, data for this column
is regarded truncated. Note that this has no correspondence
to the sign of result set column, if you need to find it out
use mysql_stmt_result_metadata.
error - where to write a truncation error if it is present.
possible error value is:
0 no truncation
1 value is out of range or buffer is too small
Please note that MYSQL_BIND also has internals members.
*/
typedef struct MYSQL_BIND {
unsigned long *length; /* output length pointer */
bool *is_null; /* Pointer to null indicator */
void *buffer; /* buffer to get/put data */
/* set this if you want to track data truncations happened during fetch */
bool *error;
unsigned char *row_ptr; /* for the current data position */
void (*store_param_func)(NET *net, struct MYSQL_BIND *param);
void (*fetch_result)(struct MYSQL_BIND *, MYSQL_FIELD *, unsigned char **row);
void (*skip_result)(struct MYSQL_BIND *, MYSQL_FIELD *, unsigned char **row);
/* output buffer length, must be set when fetching str/binary */
unsigned long buffer_length;
unsigned long offset; /* offset position for char/binary fetch */
unsigned long length_value; /* Used if length is 0 */
unsigned int param_number; /* For null count and error messages */
unsigned int pack_length; /* Internal length for packed data */
enum enum_field_types buffer_type; /* buffer type */
bool error_value; /* used if error is 0 */
bool is_unsigned; /* set if integer type is unsigned */
bool long_data_used; /* If used with mysql_send_long_data */
bool is_null_value; /* Used if is_null is 0 */
void *extension;
} MYSQL_BIND;
struct MYSQL_STMT_EXT;
/* statement handler */
typedef struct MYSQL_STMT {
struct MEM_ROOT *mem_root; /* root allocations */
LIST list; /* list to keep track of all stmts */
MYSQL *mysql; /* connection handle */
MYSQL_BIND *params; /* input parameters */
MYSQL_BIND *bind; /* output parameters */
MYSQL_FIELD *fields; /* result set metadata */
MYSQL_DATA result; /* cached result set */
MYSQL_ROWS *data_cursor; /* current row in cached result */
/*
mysql_stmt_fetch() calls this function to fetch one row (it's different
for buffered, unbuffered and cursor fetch).
*/
int (*read_row_func)(struct MYSQL_STMT *stmt, unsigned char **row);
/* copy of mysql->affected_rows after statement execution */
uint64_t affected_rows;
uint64_t insert_id; /* copy of mysql->insert_id */
unsigned long stmt_id; /* Id for prepared statement */
unsigned long flags; /* i.e. type of cursor to open */
unsigned long prefetch_rows; /* number of rows per one COM_FETCH */
/*
Copied from mysql->server_status after execute/fetch to know
server-side cursor status for this statement.
*/
unsigned int server_status;
unsigned int last_errno; /* error code */
unsigned int param_count; /* input parameter count */
unsigned int field_count; /* number of columns in result set */
enum enum_mysql_stmt_state state; /* statement state */
char last_error[MYSQL_ERRMSG_SIZE]; /* error message */
char sqlstate[SQLSTATE_LENGTH + 1];
/* Types of input parameters should be sent to server */
bool send_types_to_server;
bool bind_param_done; /* input buffers were supplied */
unsigned char bind_result_done; /* output buffers were supplied */
/* mysql_stmt_close() had to cancel this result */
bool unbuffered_fetch_cancelled;
/*
Is set to true if we need to calculate field->max_length for
metadata fields when doing mysql_stmt_store_result.
*/
bool update_max_length;
struct MYSQL_STMT_EXT *extension;
} MYSQL_STMT;
enum enum_stmt_attr_type {
/*
When doing mysql_stmt_store_result calculate max_length attribute
of statement metadata. This is to be consistent with the old API,
where this was done automatically.
In the new API we do that only by request because it slows down
mysql_stmt_store_result sufficiently.
*/
STMT_ATTR_UPDATE_MAX_LENGTH,
/*
unsigned long with combination of cursor flags (read only, for update,
etc)
*/
STMT_ATTR_CURSOR_TYPE,
/*
Amount of rows to retrieve from server per one fetch if using cursors.
Accepts unsigned long attribute in the range 1 - ulong_max
*/
STMT_ATTR_PREFETCH_ROWS
};
bool STDCALL mysql_bind_param(MYSQL *mysql, unsigned n_params,
MYSQL_BIND *binds, const char **names);
MYSQL_STMT *STDCALL mysql_stmt_init(MYSQL *mysql);
int STDCALL mysql_stmt_prepare(MYSQL_STMT *stmt, const char *query,
unsigned long length);
int STDCALL mysql_stmt_execute(MYSQL_STMT *stmt);
int STDCALL mysql_stmt_fetch(MYSQL_STMT *stmt);
int STDCALL mysql_stmt_fetch_column(MYSQL_STMT *stmt, MYSQL_BIND *bind_arg,
unsigned int column, unsigned long offset);
int STDCALL mysql_stmt_store_result(MYSQL_STMT *stmt);
unsigned long STDCALL mysql_stmt_param_count(MYSQL_STMT *stmt);
bool STDCALL mysql_stmt_attr_set(MYSQL_STMT *stmt,
enum enum_stmt_attr_type attr_type,
const void *attr);
bool STDCALL mysql_stmt_attr_get(MYSQL_STMT *stmt,
enum enum_stmt_attr_type attr_type,
void *attr);
bool STDCALL mysql_stmt_bind_param(MYSQL_STMT *stmt, MYSQL_BIND *bnd);
bool STDCALL mysql_stmt_bind_result(MYSQL_STMT *stmt, MYSQL_BIND *bnd);
bool STDCALL mysql_stmt_close(MYSQL_STMT *stmt);
bool STDCALL mysql_stmt_reset(MYSQL_STMT *stmt);
bool STDCALL mysql_stmt_free_result(MYSQL_STMT *stmt);
bool STDCALL mysql_stmt_send_long_data(MYSQL_STMT *stmt,
unsigned int param_number,
const char *data, unsigned long length);
MYSQL_RES *STDCALL mysql_stmt_result_metadata(MYSQL_STMT *stmt);
MYSQL_RES *STDCALL mysql_stmt_param_metadata(MYSQL_STMT *stmt);
unsigned int STDCALL mysql_stmt_errno(MYSQL_STMT *stmt);
const char *STDCALL mysql_stmt_error(MYSQL_STMT *stmt);
const char *STDCALL mysql_stmt_sqlstate(MYSQL_STMT *stmt);
MYSQL_ROW_OFFSET STDCALL mysql_stmt_row_seek(MYSQL_STMT *stmt,
MYSQL_ROW_OFFSET offset);
MYSQL_ROW_OFFSET STDCALL mysql_stmt_row_tell(MYSQL_STMT *stmt);
void STDCALL mysql_stmt_data_seek(MYSQL_STMT *stmt, uint64_t offset);
uint64_t STDCALL mysql_stmt_num_rows(MYSQL_STMT *stmt);
uint64_t STDCALL mysql_stmt_affected_rows(MYSQL_STMT *stmt);
uint64_t STDCALL mysql_stmt_insert_id(MYSQL_STMT *stmt);
unsigned int STDCALL mysql_stmt_field_count(MYSQL_STMT *stmt);
bool STDCALL mysql_commit(MYSQL *mysql);
bool STDCALL mysql_rollback(MYSQL *mysql);
bool STDCALL mysql_autocommit(MYSQL *mysql, bool auto_mode);
bool STDCALL mysql_more_results(MYSQL *mysql);
int STDCALL mysql_next_result(MYSQL *mysql);
int STDCALL mysql_stmt_next_result(MYSQL_STMT *stmt);
void STDCALL mysql_close(MYSQL *sock);
/* Public key reset */
void STDCALL mysql_reset_server_public_key(void);
/* status return codes */
#define MYSQL_NO_DATA 100
#define MYSQL_DATA_TRUNCATED 101
#define mysql_reload(mysql) mysql_refresh((mysql), REFRESH_GRANT)
#define HAVE_MYSQL_REAL_CONNECT
MYSQL *STDCALL mysql_real_connect_dns_srv(MYSQL *mysql,
const char *dns_srv_name,
const char *user, const char *passwd,
const char *db,
unsigned long client_flag);
#ifdef __cplusplus
}
#endif
#endif /* _mysql_h */
+1193
View File
File diff suppressed because it is too large Load Diff
+90
View File
@@ -0,0 +1,90 @@
/* Copyright (c) 2004, 2021, Oracle and/or its affiliates.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License, version 2.0,
as published by the Free Software Foundation.
This program is also distributed with certain software (including
but not limited to OpenSSL) that is licensed under separate terms,
as designated in a particular file or component or in included license
documentation. The authors of MySQL hereby grant you an additional
permission to link the program and your derivative works with the
separately licensed software that they have included with MySQL.
Without limiting anything contained in the foregoing, this file,
which is part of C Driver for MySQL (Connector/C), is also subject to the
Universal FOSS Exception, version 1.0, a copy of which can be found at
http://oss.oracle.com/licenses/universal-foss-exception.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License, version 2.0, for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
#ifndef _mysql_time_h_
#define _mysql_time_h_
/**
@file include/mysql_time.h
Time declarations shared between the server and client API:
you should not add anything to this header unless it's used
(and hence should be visible) in mysql.h.
If you're looking for a place to add new time-related declaration,
it's most likely my_time.h. See also "C API Handling of Date
and Time Values" chapter in documentation.
*/
// Do not not pull in the server header "my_inttypes.h" from client code.
// IWYU pragma: no_include "my_inttypes.h"
enum enum_mysql_timestamp_type {
MYSQL_TIMESTAMP_NONE = -2,
MYSQL_TIMESTAMP_ERROR = -1,
/// Stores year, month and day components.
MYSQL_TIMESTAMP_DATE = 0,
/**
Stores all date and time components.
Value is in UTC for `TIMESTAMP` type.
Value is in local time zone for `DATETIME` type.
*/
MYSQL_TIMESTAMP_DATETIME = 1,
/// Stores hour, minute, second and microsecond.
MYSQL_TIMESTAMP_TIME = 2,
/**
A temporary type for `DATETIME` or `TIMESTAMP` types equipped with time
zone information. After the time zone information is reconciled, the type is
converted to MYSQL_TIMESTAMP_DATETIME.
*/
MYSQL_TIMESTAMP_DATETIME_TZ = 3
};
/*
Structure which is used to represent datetime values inside MySQL.
We assume that values in this structure are normalized, i.e. year <= 9999,
month <= 12, day <= 31, hour <= 23, hour <= 59, hour <= 59. Many functions
in server such as my_system_gmt_sec() or make_time() family of functions
rely on this (actually now usage of make_*() family relies on a bit weaker
restriction). Also functions that produce MYSQL_TIME as result ensure this.
There is one exception to this rule though if this structure holds time
value (time_type == MYSQL_TIMESTAMP_TIME) days and hour member can hold
bigger values.
*/
typedef struct MYSQL_TIME {
unsigned int year, month, day, hour, minute, second;
unsigned long second_part; /**< microseconds */
bool neg;
enum enum_mysql_timestamp_type time_type;
/// The time zone displacement, specified in seconds.
int time_zone_displacement;
} MYSQL_TIME;
#endif /* _mysql_time_h_ */
+31
View File
@@ -0,0 +1,31 @@
/* Copyright Abandoned 1996,1999 TCX DataKonsult AB & Monty Program KB
& Detron HB, 1996, 1999-2004, 2007 MySQL AB.
This file is public domain and comes with NO WARRANTY of any kind
*/
/* Version numbers for protocol & mysqld */
#ifndef _mysql_version_h
#define _mysql_version_h
#define PROTOCOL_VERSION 10
#define MYSQL_SERVER_VERSION "8.0.27"
#define MYSQL_BASE_VERSION "mysqld-8.0"
#define MYSQL_SERVER_SUFFIX_DEF "-0ubuntu0.21.10.1"
#define MYSQL_VERSION_ID 80027
#define MYSQL_PORT 3306
#define MYSQL_ADMIN_PORT 33062
#define MYSQL_PORT_DEFAULT 0
#define MYSQL_UNIX_ADDR "/var/run/mysqld/mysqld.sock"
#define MYSQL_CONFIG_NAME "my"
#define MYSQL_PERSIST_CONFIG_NAME "mysqld-auto"
#define MYSQL_COMPILATION_COMMENT "(Ubuntu)"
#define MYSQL_COMPILATION_COMMENT_SERVER "(Ubuntu)"
#define LIBMYSQL_VERSION "8.0.27"
#define LIBMYSQL_VERSION_ID 80027
#ifndef LICENSE
#define LICENSE GPL
#endif /* LICENSE */
#endif /* _mysql_version_h */
+5562
View File
File diff suppressed because it is too large Load Diff
+99
View File
@@ -0,0 +1,99 @@
/*
* Copyright (c) 2016, 2021, Oracle and/or its affiliates.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2.0,
* as published by the Free Software Foundation.
*
* This program is also distributed with certain software (including
* but not limited to OpenSSL) that is licensed under separate terms,
* as designated in a particular file or component or in included license
* documentation. The authors of MySQL hereby grant you an additional
* permission to link the program and your derivative works with the
* separately licensed software that they have included with MySQL.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License, version 2.0, for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/* Autogenerated file, please don't edit */
#include "mysqlx_error.h"
{"ER_X_BAD_MESSAGE", ER_X_BAD_MESSAGE, "", NULL, NULL, 0 },
{"ER_X_CAPABILITIES_PREPARE_FAILED", ER_X_CAPABILITIES_PREPARE_FAILED, "", NULL, NULL, 0 },
{"ER_X_CAPABILITY_NOT_FOUND", ER_X_CAPABILITY_NOT_FOUND, "", NULL, NULL, 0 },
{"ER_X_INVALID_PROTOCOL_DATA", ER_X_INVALID_PROTOCOL_DATA, "", NULL, NULL, 0 },
{"ER_X_BAD_CONNECTION_SESSION_ATTRIBUTE_VALUE_LENGTH", ER_X_BAD_CONNECTION_SESSION_ATTRIBUTE_VALUE_LENGTH, "", NULL, NULL, 0 },
{"ER_X_BAD_CONNECTION_SESSION_ATTRIBUTE_KEY_LENGTH", ER_X_BAD_CONNECTION_SESSION_ATTRIBUTE_KEY_LENGTH, "", NULL, NULL, 0 },
{"ER_X_BAD_CONNECTION_SESSION_ATTRIBUTE_EMPTY_KEY", ER_X_BAD_CONNECTION_SESSION_ATTRIBUTE_EMPTY_KEY, "", NULL, NULL, 0 },
{"ER_X_BAD_CONNECTION_SESSION_ATTRIBUTE_LENGTH", ER_X_BAD_CONNECTION_SESSION_ATTRIBUTE_LENGTH, "", NULL, NULL, 0 },
{"ER_X_BAD_CONNECTION_SESSION_ATTRIBUTE_TYPE", ER_X_BAD_CONNECTION_SESSION_ATTRIBUTE_TYPE, "", NULL, NULL, 0 },
{"ER_X_CAPABILITY_SET_NOT_ALLOWED", ER_X_CAPABILITY_SET_NOT_ALLOWED, "", NULL, NULL, 0 },
{"ER_X_SERVICE_ERROR", ER_X_SERVICE_ERROR, "", NULL, NULL, 0 },
{"ER_X_SESSION", ER_X_SESSION, "", NULL, NULL, 0 },
{"ER_X_INVALID_ARGUMENT", ER_X_INVALID_ARGUMENT, "", NULL, NULL, 0 },
{"ER_X_MISSING_ARGUMENT", ER_X_MISSING_ARGUMENT, "", NULL, NULL, 0 },
{"ER_X_BAD_INSERT_DATA", ER_X_BAD_INSERT_DATA, "", NULL, NULL, 0 },
{"ER_X_CMD_NUM_ARGUMENTS", ER_X_CMD_NUM_ARGUMENTS, "", NULL, NULL, 0 },
{"ER_X_CMD_ARGUMENT_TYPE", ER_X_CMD_ARGUMENT_TYPE, "", NULL, NULL, 0 },
{"ER_X_CMD_ARGUMENT_VALUE", ER_X_CMD_ARGUMENT_VALUE, "", NULL, NULL, 0 },
{"ER_X_BAD_UPSERT_DATA", ER_X_BAD_UPSERT_DATA, "", NULL, NULL, 0 },
{"ER_X_DUPLICATED_CAPABILITIES", ER_X_DUPLICATED_CAPABILITIES, "", NULL, NULL, 0 },
{"ER_X_CMD_ARGUMENT_OBJECT_EMPTY", ER_X_CMD_ARGUMENT_OBJECT_EMPTY, "", NULL, NULL, 0 },
{"ER_X_CMD_INVALID_ARGUMENT", ER_X_CMD_INVALID_ARGUMENT, "", NULL, NULL, 0 },
{"ER_X_BAD_UPDATE_DATA", ER_X_BAD_UPDATE_DATA, "", NULL, NULL, 0 },
{"ER_X_BAD_TYPE_OF_UPDATE", ER_X_BAD_TYPE_OF_UPDATE, "", NULL, NULL, 0 },
{"ER_X_BAD_COLUMN_TO_UPDATE", ER_X_BAD_COLUMN_TO_UPDATE, "", NULL, NULL, 0 },
{"ER_X_BAD_MEMBER_TO_UPDATE", ER_X_BAD_MEMBER_TO_UPDATE, "", NULL, NULL, 0 },
{"ER_X_BAD_STATEMENT_ID", ER_X_BAD_STATEMENT_ID, "", NULL, NULL, 0 },
{"ER_X_BAD_CURSOR_ID", ER_X_BAD_CURSOR_ID, "", NULL, NULL, 0 },
{"ER_X_BAD_SCHEMA", ER_X_BAD_SCHEMA, "", NULL, NULL, 0 },
{"ER_X_BAD_TABLE", ER_X_BAD_TABLE, "", NULL, NULL, 0 },
{"ER_X_BAD_PROJECTION", ER_X_BAD_PROJECTION, "", NULL, NULL, 0 },
{"ER_X_DOC_ID_MISSING", ER_X_DOC_ID_MISSING, "", NULL, NULL, 0 },
{"ER_X_DUPLICATE_ENTRY", ER_X_DUPLICATE_ENTRY, "", NULL, NULL, 0 },
{"ER_X_DOC_REQUIRED_FIELD_MISSING", ER_X_DOC_REQUIRED_FIELD_MISSING, "", NULL, NULL, 0 },
{"ER_X_PROJ_BAD_KEY_NAME", ER_X_PROJ_BAD_KEY_NAME, "", NULL, NULL, 0 },
{"ER_X_BAD_DOC_PATH", ER_X_BAD_DOC_PATH, "", NULL, NULL, 0 },
{"ER_X_CURSOR_EXISTS", ER_X_CURSOR_EXISTS, "", NULL, NULL, 0 },
{"ER_X_CURSOR_REACHED_EOF", ER_X_CURSOR_REACHED_EOF, "", NULL, NULL, 0 },
{"ER_X_PREPARED_STATMENT_CAN_HAVE_ONE_CURSOR", ER_X_PREPARED_STATMENT_CAN_HAVE_ONE_CURSOR, "", NULL, NULL, 0 },
{"ER_X_PREPARED_EXECUTE_ARGUMENT_NOT_SUPPORTED", ER_X_PREPARED_EXECUTE_ARGUMENT_NOT_SUPPORTED, "", NULL, NULL, 0 },
{"ER_X_PREPARED_EXECUTE_ARGUMENT_CONSISTENCY", ER_X_PREPARED_EXECUTE_ARGUMENT_CONSISTENCY, "", NULL, NULL, 0 },
{"ER_X_EXPR_BAD_OPERATOR", ER_X_EXPR_BAD_OPERATOR, "", NULL, NULL, 0 },
{"ER_X_EXPR_BAD_NUM_ARGS", ER_X_EXPR_BAD_NUM_ARGS, "", NULL, NULL, 0 },
{"ER_X_EXPR_MISSING_ARG", ER_X_EXPR_MISSING_ARG, "", NULL, NULL, 0 },
{"ER_X_EXPR_BAD_TYPE_VALUE", ER_X_EXPR_BAD_TYPE_VALUE, "", NULL, NULL, 0 },
{"ER_X_EXPR_BAD_VALUE", ER_X_EXPR_BAD_VALUE, "", NULL, NULL, 0 },
{"ER_X_INVALID_COLLECTION", ER_X_INVALID_COLLECTION, "", NULL, NULL, 0 },
{"ER_X_INVALID_ADMIN_COMMAND", ER_X_INVALID_ADMIN_COMMAND, "", NULL, NULL, 0 },
{"ER_X_EXPECT_NOT_OPEN", ER_X_EXPECT_NOT_OPEN, "", NULL, NULL, 0 },
{"ER_X_EXPECT_NO_ERROR_FAILED", ER_X_EXPECT_NO_ERROR_FAILED, "", NULL, NULL, 0 },
{"ER_X_EXPECT_BAD_CONDITION", ER_X_EXPECT_BAD_CONDITION, "", NULL, NULL, 0 },
{"ER_X_EXPECT_BAD_CONDITION_VALUE", ER_X_EXPECT_BAD_CONDITION_VALUE, "", NULL, NULL, 0 },
{"ER_X_INVALID_NAMESPACE", ER_X_INVALID_NAMESPACE, "", NULL, NULL, 0 },
{"ER_X_BAD_NOTICE", ER_X_BAD_NOTICE, "", NULL, NULL, 0 },
{"ER_X_CANNOT_DISABLE_NOTICE", ER_X_CANNOT_DISABLE_NOTICE, "", NULL, NULL, 0 },
{"ER_X_BAD_CONFIGURATION", ER_X_BAD_CONFIGURATION, "", NULL, NULL, 0 },
{"ER_X_MYSQLX_ACCOUNT_MISSING_PERMISSIONS", ER_X_MYSQLX_ACCOUNT_MISSING_PERMISSIONS, "", NULL, NULL, 0 },
{"ER_X_EXPECT_FIELD_EXISTS_FAILED", ER_X_EXPECT_FIELD_EXISTS_FAILED, "", NULL, NULL, 0 },
{"ER_X_BAD_LOCKING", ER_X_BAD_LOCKING, "", NULL, NULL, 0 },
{"ER_X_FRAME_COMPRESSION_DISABLED", ER_X_FRAME_COMPRESSION_DISABLED, "", NULL, NULL, 0 },
{"ER_X_DECOMPRESSION_FAILED", ER_X_DECOMPRESSION_FAILED, "", NULL, NULL, 0 },
{"ER_X_BAD_COMPRESSED_FRAME", ER_X_BAD_COMPRESSED_FRAME, "", NULL, NULL, 0 },
{"ER_X_CAPABILITY_COMPRESSION_INVALID_ALGORITHM", ER_X_CAPABILITY_COMPRESSION_INVALID_ALGORITHM, "", NULL, NULL, 0 },
{"ER_X_CAPABILITY_COMPRESSION_INVALID_SERVER_STYLE", ER_X_CAPABILITY_COMPRESSION_INVALID_SERVER_STYLE, "", NULL, NULL, 0 },
{"ER_X_CAPABILITY_COMPRESSION_INVALID_CLIENT_STYLE", ER_X_CAPABILITY_COMPRESSION_INVALID_CLIENT_STYLE, "", NULL, NULL, 0 },
{"ER_X_CAPABILITY_COMPRESSION_INVALID_OPTION", ER_X_CAPABILITY_COMPRESSION_INVALID_OPTION, "", NULL, NULL, 0 },
{"ER_X_CAPABILITY_COMPRESSION_MISSING_REQUIRED_FIELDS", ER_X_CAPABILITY_COMPRESSION_MISSING_REQUIRED_FIELDS, "", NULL, NULL, 0 },
{"ER_X_DOCUMENT_DOESNT_MATCH_EXPECTED_SCHEMA", ER_X_DOCUMENT_DOESNT_MATCH_EXPECTED_SCHEMA, "", NULL, NULL, 0 },
{"ER_X_COLLECTION_OPTION_DOESNT_EXISTS", ER_X_COLLECTION_OPTION_DOESNT_EXISTS, "", NULL, NULL, 0 },
{"ER_X_INVALID_VALIDATION_SCHEMA", ER_X_INVALID_VALIDATION_SCHEMA, "", NULL, NULL, 0 },
+99
View File
@@ -0,0 +1,99 @@
/* Copyright (c) 2015, 2021, Oracle and/or its affiliates.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License, version 2.0,
as published by the Free Software Foundation.
This program is also distributed with certain software (including
but not limited to OpenSSL) that is licensed under separate terms,
as designated in a particular file or component or in included license
documentation. The authors of MySQL hereby grant you an additional
permission to link the program and your derivative works with the
separately licensed software that they have included with MySQL.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License, version 2.0, for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
#ifndef _MYSQLX_ERROR_H_
#define _MYSQLX_ERROR_H_
#define ER_X_BAD_MESSAGE 5000
#define ER_X_CAPABILITIES_PREPARE_FAILED 5001
#define ER_X_CAPABILITY_NOT_FOUND 5002
#define ER_X_INVALID_PROTOCOL_DATA 5003
#define ER_X_BAD_CONNECTION_SESSION_ATTRIBUTE_VALUE_LENGTH 5004
#define ER_X_BAD_CONNECTION_SESSION_ATTRIBUTE_KEY_LENGTH 5005
#define ER_X_BAD_CONNECTION_SESSION_ATTRIBUTE_EMPTY_KEY 5006
#define ER_X_BAD_CONNECTION_SESSION_ATTRIBUTE_LENGTH 5007
#define ER_X_BAD_CONNECTION_SESSION_ATTRIBUTE_TYPE 5008
#define ER_X_CAPABILITY_SET_NOT_ALLOWED 5009
#define ER_X_SERVICE_ERROR 5010
#define ER_X_SESSION 5011
#define ER_X_INVALID_ARGUMENT 5012
#define ER_X_MISSING_ARGUMENT 5013
#define ER_X_BAD_INSERT_DATA 5014
#define ER_X_CMD_NUM_ARGUMENTS 5015
#define ER_X_CMD_ARGUMENT_TYPE 5016
#define ER_X_CMD_ARGUMENT_VALUE 5017
#define ER_X_BAD_UPSERT_DATA 5018
#define ER_X_DUPLICATED_CAPABILITIES 5019
#define ER_X_CMD_ARGUMENT_OBJECT_EMPTY 5020
#define ER_X_CMD_INVALID_ARGUMENT 5021
#define ER_X_BAD_UPDATE_DATA 5050
#define ER_X_BAD_TYPE_OF_UPDATE 5051
#define ER_X_BAD_COLUMN_TO_UPDATE 5052
#define ER_X_BAD_MEMBER_TO_UPDATE 5053
#define ER_X_BAD_STATEMENT_ID 5110
#define ER_X_BAD_CURSOR_ID 5111
#define ER_X_BAD_SCHEMA 5112
#define ER_X_BAD_TABLE 5113
#define ER_X_BAD_PROJECTION 5114
#define ER_X_DOC_ID_MISSING 5115
#define ER_X_DUPLICATE_ENTRY 5116
#define ER_X_DOC_REQUIRED_FIELD_MISSING 5117
#define ER_X_PROJ_BAD_KEY_NAME 5120
#define ER_X_BAD_DOC_PATH 5121
#define ER_X_CURSOR_EXISTS 5122
#define ER_X_CURSOR_REACHED_EOF 5123
#define ER_X_PREPARED_STATMENT_CAN_HAVE_ONE_CURSOR 5131
#define ER_X_PREPARED_EXECUTE_ARGUMENT_NOT_SUPPORTED 5133
#define ER_X_PREPARED_EXECUTE_ARGUMENT_CONSISTENCY 5134
#define ER_X_EXPR_BAD_OPERATOR 5150
#define ER_X_EXPR_BAD_NUM_ARGS 5151
#define ER_X_EXPR_MISSING_ARG 5152
#define ER_X_EXPR_BAD_TYPE_VALUE 5153
#define ER_X_EXPR_BAD_VALUE 5154
#define ER_X_INVALID_COLLECTION 5156
#define ER_X_INVALID_ADMIN_COMMAND 5157
#define ER_X_EXPECT_NOT_OPEN 5158
#define ER_X_EXPECT_NO_ERROR_FAILED 5159
#define ER_X_EXPECT_BAD_CONDITION 5160
#define ER_X_EXPECT_BAD_CONDITION_VALUE 5161
#define ER_X_INVALID_NAMESPACE 5162
#define ER_X_BAD_NOTICE 5163
#define ER_X_CANNOT_DISABLE_NOTICE 5164
#define ER_X_BAD_CONFIGURATION 5165
#define ER_X_MYSQLX_ACCOUNT_MISSING_PERMISSIONS 5167
#define ER_X_EXPECT_FIELD_EXISTS_FAILED 5168
#define ER_X_BAD_LOCKING 5169
#define ER_X_FRAME_COMPRESSION_DISABLED 5170
#define ER_X_DECOMPRESSION_FAILED 5171
#define ER_X_BAD_COMPRESSED_FRAME 5174
#define ER_X_CAPABILITY_COMPRESSION_INVALID_ALGORITHM 5175
#define ER_X_CAPABILITY_COMPRESSION_INVALID_SERVER_STYLE 5176
#define ER_X_CAPABILITY_COMPRESSION_INVALID_CLIENT_STYLE 5177
#define ER_X_CAPABILITY_COMPRESSION_INVALID_OPTION 5178
#define ER_X_CAPABILITY_COMPRESSION_MISSING_REQUIRED_FIELDS 5179
#define ER_X_DOCUMENT_DOESNT_MATCH_EXPECTED_SCHEMA 5180
#define ER_X_COLLECTION_OPTION_DOESNT_EXISTS 5181
#define ER_X_INVALID_VALIDATION_SCHEMA 5182
#endif // _MYSQLX_ERROR_H_
+44
View File
@@ -0,0 +1,44 @@
/*
* Copyright (c) 2016, 2021, Oracle and/or its affiliates.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2.0,
* as published by the Free Software Foundation.
*
* This program is also distributed with certain software (including
* but not limited to OpenSSL) that is licensed under separate terms,
* as designated in a particular file or component or in included license
* documentation. The authors of MySQL hereby grant you an additional
* permission to link the program and your derivative works with the
* separately licensed software that they have included with MySQL.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License, version 2.0, for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/* Version numbers for X Plugin */
#ifndef _MYSQLX_VERSION_H_
#define _MYSQLX_VERSION_H_
#define MYSQLX_PLUGIN_VERSION_MAJOR 1
#define MYSQLX_PLUGIN_VERSION_MINOR 0
#define MYSQLX_PLUGIN_VERSION_PATCH 2
#define MYSQLX_PLUGIN_NAME "mysqlx"
#define MYSQLX_STATUS_VARIABLE_PREFIX(NAME) "Mysqlx_" NAME
#define MYSQLX_SYSTEM_VARIABLE_PREFIX(NAME) "mysqlx_" NAME
#define MYSQLX_TCP_PORT 33060U
#define MYSQLX_UNIX_ADDR "/var/run/mysqld/mysqlx.sock"
#define MYSQLX_PLUGIN_VERSION ( (MYSQLX_PLUGIN_VERSION_MAJOR << 8) | MYSQLX_PLUGIN_VERSION_MINOR )
#define MYSQLX_PLUGIN_VERSION_STRING "1.0.2"
#endif // _MYSQLX_VERSION_H_
+187
View File
@@ -0,0 +1,187 @@
#ifndef MYSQL_PLUGIN_AUTH_COMMON_INCLUDED
/* Copyright (c) 2010, 2021, Oracle and/or its affiliates.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License, version 2.0,
as published by the Free Software Foundation.
This program is also distributed with certain software (including
but not limited to OpenSSL) that is licensed under separate terms,
as designated in a particular file or component or in included license
documentation. The authors of MySQL hereby grant you an additional
permission to link the program and your derivative works with the
separately licensed software that they have included with MySQL.
Without limiting anything contained in the foregoing, this file,
which is part of C Driver for MySQL (Connector/C), is also subject to the
Universal FOSS Exception, version 1.0, a copy of which can be found at
http://oss.oracle.com/licenses/universal-foss-exception.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License, version 2.0, for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/**
@file include/mysql/plugin_auth_common.h
This file defines constants and data structures that are the same for
both client- and server-side authentication plugins.
*/
#define MYSQL_PLUGIN_AUTH_COMMON_INCLUDED
/** the max allowed length for a user name */
#define MYSQL_USERNAME_LENGTH 96
/**
return values of the plugin authenticate_user() method.
*/
/**
Authentication failed, plugin internal error.
An error occurred in the authentication plugin itself.
These errors are reported in table performance_schema.host_cache,
column COUNT_AUTH_PLUGIN_ERRORS.
*/
#define CR_AUTH_PLUGIN_ERROR 3
/**
Authentication failed, client server handshake.
An error occurred during the client server handshake.
These errors are reported in table performance_schema.host_cache,
column COUNT_HANDSHAKE_ERRORS.
*/
#define CR_AUTH_HANDSHAKE 2
/**
Authentication failed, user credentials.
For example, wrong passwords.
These errors are reported in table performance_schema.host_cache,
column COUNT_AUTHENTICATION_ERRORS.
*/
#define CR_AUTH_USER_CREDENTIALS 1
/**
Authentication failed. Additionally, all other CR_xxx values
(libmysql error code) can be used too.
The client plugin may set the error code and the error message directly
in the MYSQL structure and return CR_ERROR. If a CR_xxx specific error
code was returned, an error message in the MYSQL structure will be
overwritten. If CR_ERROR is returned without setting the error in MYSQL,
CR_UNKNOWN_ERROR will be user.
*/
#define CR_ERROR 0
/**
Authentication (client part) was successful. It does not mean that the
authentication as a whole was successful, usually it only means
that the client was able to send the user name and the password to the
server. If CR_OK is returned, the libmysql reads the next packet expecting
it to be one of OK, ERROR, or CHANGE_PLUGIN packets.
*/
#define CR_OK -1
/**
Authentication was successful.
It means that the client has done its part successfully and also that
a plugin has read the last packet (one of OK, ERROR, CHANGE_PLUGIN).
In this case, libmysql will not read a packet from the server,
but it will use the data at mysql->net.read_pos.
A plugin may return this value if the number of roundtrips in the
authentication protocol is not known in advance, and the client plugin
needs to read one packet more to determine if the authentication is finished
or not.
*/
#define CR_OK_HANDSHAKE_COMPLETE -2
/**
Authentication was successful with limited operations.
It means that the both client and server side plugins decided to allow
authentication with very limited operations ALTER USER to do registration.
*/
#define CR_OK_AUTH_IN_SANDBOX_MODE -3
/**
Flag to be passed back to server from authentication plugins via
authenticated_as when proxy mapping should be done by the server.
*/
#define PROXY_FLAG 0
/*
We need HANDLE definition if on Windows. Define WIN32_LEAN_AND_MEAN (if
not already done) to minimize amount of imported declarations.
*/
#ifdef _WIN32
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#endif
struct MYSQL_PLUGIN_VIO_INFO {
enum {
MYSQL_VIO_INVALID,
MYSQL_VIO_TCP,
MYSQL_VIO_SOCKET,
MYSQL_VIO_PIPE,
MYSQL_VIO_MEMORY
} protocol;
int socket; /**< it's set, if the protocol is SOCKET or TCP */
#ifdef _WIN32
HANDLE handle; /**< it's set, if the protocol is PIPE or MEMORY */
#endif
};
/* state of an asynchronous operation */
enum net_async_status {
NET_ASYNC_COMPLETE = 0,
NET_ASYNC_NOT_READY,
NET_ASYNC_ERROR,
NET_ASYNC_COMPLETE_NO_MORE_RESULTS
};
/**
Provides plugin access to communication channel
*/
typedef struct MYSQL_PLUGIN_VIO {
/**
Plugin provides a pointer reference and this function sets it to the
contents of any incoming packet. Returns the packet length, or -1 if
the plugin should terminate.
*/
int (*read_packet)(struct MYSQL_PLUGIN_VIO *vio, unsigned char **buf);
/**
Plugin provides a buffer with data and the length and this
function sends it as a packet. Returns 0 on success, 1 on failure.
*/
int (*write_packet)(struct MYSQL_PLUGIN_VIO *vio, const unsigned char *packet,
int packet_len);
/**
Fills in a MYSQL_PLUGIN_VIO_INFO structure, providing the information
about the connection.
*/
void (*info)(struct MYSQL_PLUGIN_VIO *vio,
struct MYSQL_PLUGIN_VIO_INFO *info);
/**
Non blocking version of read_packet. This function points buf to starting
position of incoming packet. When this function returns NET_ASYNC_NOT_READY
plugin should call this function again until all incoming packets are read.
If return code is NET_ASYNC_COMPLETE, plugin can do further processing of
read packets.
*/
enum net_async_status (*read_packet_nonblocking)(struct MYSQL_PLUGIN_VIO *vio,
unsigned char **buf,
int *result);
/**
Non blocking version of write_packet. Sends data available in pkt of length
pkt_len to server in asynchrnous way.
*/
enum net_async_status (*write_packet_nonblocking)(
struct MYSQL_PLUGIN_VIO *vio, const unsigned char *pkt, int pkt_len,
int *result);
} MYSQL_PLUGIN_VIO;
#endif
+90
View File
@@ -0,0 +1,90 @@
/* Copyright (c) 2017, 2021, Oracle and/or its affiliates.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License, version 2.0,
as published by the Free Software Foundation.
This program is also distributed with certain software (including
but not limited to OpenSSL) that is licensed under separate terms,
as designated in a particular file or component or in included license
documentation. The authors of MySQL hereby grant you an additional
permission to link the program and your derivative works with the
separately licensed software that they have included with MySQL.
Without limiting anything contained in the foregoing, this file,
which is part of C Driver for MySQL (Connector/C), is also subject to the
Universal FOSS Exception, version 1.0, a copy of which can be found at
http://oss.oracle.com/licenses/universal-foss-exception.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License, version 2.0, for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
#ifndef UDF_REGISTRATION_TYPES_H
#define UDF_REGISTRATION_TYPES_H
#ifndef MYSQL_ABI_CHECK
#include <stdbool.h>
#endif
/**
Type of the user defined function return slot and arguments
*/
enum Item_result {
INVALID_RESULT = -1, /** not valid for UDFs */
STRING_RESULT = 0, /** char * */
REAL_RESULT, /** double */
INT_RESULT, /** long long */
ROW_RESULT, /** not valid for UDFs */
DECIMAL_RESULT /** char *, to be converted to/from a decimal */
};
typedef struct UDF_ARGS {
unsigned int arg_count; /**< Number of arguments */
enum Item_result *arg_type; /**< Pointer to item_results */
char **args; /**< Pointer to argument */
unsigned long *lengths; /**< Length of string arguments */
char *maybe_null; /**< Set to 1 for all maybe_null args */
char **attributes; /**< Pointer to attribute name */
unsigned long *attribute_lengths; /**< Length of attribute arguments */
void *extension;
} UDF_ARGS;
/**
Information about the result of a user defined function
@todo add a notion for determinism of the UDF.
@sa Item_udf_func::update_used_tables()
*/
typedef struct UDF_INIT {
bool maybe_null; /** 1 if function can return NULL */
unsigned int decimals; /** for real functions */
unsigned long max_length; /** For string functions */
char *ptr; /** free pointer for function data */
bool const_item; /** 1 if function always returns the same value */
void *extension;
} UDF_INIT;
enum Item_udftype { UDFTYPE_FUNCTION = 1, UDFTYPE_AGGREGATE };
typedef void (*Udf_func_clear)(UDF_INIT *, unsigned char *, unsigned char *);
typedef void (*Udf_func_add)(UDF_INIT *, UDF_ARGS *, unsigned char *,
unsigned char *);
typedef void (*Udf_func_deinit)(UDF_INIT *);
typedef bool (*Udf_func_init)(UDF_INIT *, UDF_ARGS *, char *);
typedef void (*Udf_func_any)(void);
typedef double (*Udf_func_double)(UDF_INIT *, UDF_ARGS *, unsigned char *,
unsigned char *);
typedef long long (*Udf_func_longlong)(UDF_INIT *, UDF_ARGS *, unsigned char *,
unsigned char *);
typedef char *(*Udf_func_string)(UDF_INIT *, UDF_ARGS *, char *,
unsigned long *, unsigned char *,
unsigned char *);
#endif /* UDF_REGISTRATION_TYPES_H */
+8
View File
@@ -0,0 +1,8 @@
Version 0.1.3 on 2013-02-10:
* Included required C header files.
Version 0.1.2 on 2011-11-30:
* Renamed stdin, stdout, stderr to in, out, err.
Version 0.1.1 on 2009-08-12:
* First release.
+34
View File
@@ -0,0 +1,34 @@
CMAKE_MINIMUM_REQUIRED( VERSION 2.6 )
PROJECT( fcgicc CXX )
SET( PROJECT_VERSION 0.1.3 )
SET( CMAKE_BUILD_TYPE RELEASE )
SET( CMAKE_INSTALL_PREFIX ${PREFIX} )
FIND_PATH( FCGI_INCLUDE_DIR fastcgi.h )
IF( NOT FCGI_INCLUDE_DIR )
FIND_PATH( FCGI_INCLUDE_DIR fastcgi.h ${PROJECT_SOURCE_DIR}/fastcgi_devkit )
ENDIF()
INCLUDE_DIRECTORIES( ${FCGI_INCLUDE_DIR} )
ADD_SUBDIRECTORY( src )
ADD_SUBDIRECTORY( test EXCLUDE_FROM_ALL )
INSTALL( FILES LICENSE.txt README.txt DESTINATION share/doc/${PROJECT_NAME} )
SET( DIST_FILE ${PROJECT_NAME}-${PROJECT_VERSION} )
ADD_CUSTOM_TARGET( dist ln -sf ${PROJECT_SOURCE_DIR} ${DIST_FILE} &&
tar cjf ${DIST_FILE}.tar.bz2
${DIST_FILE}/LICENSE.txt
${DIST_FILE}/README.txt
${DIST_FILE}/CHANGES.txt
${DIST_FILE}/CMakeLists.txt
${DIST_FILE}/fastcgi_devkit/LICENSE.TERMS
${DIST_FILE}/fastcgi_devkit/fastcgi.h
${DIST_FILE}/src/fcgicc.cc
${DIST_FILE}/src/fcgicc.h
${DIST_FILE}/src/CMakeLists.txt
${DIST_FILE}/test/test1.cc
${DIST_FILE}/test/test2.cc
${DIST_FILE}/test/lighttpd.conf
${DIST_FILE}/test/CMakeLists.txt )
+25
View File
@@ -0,0 +1,25 @@
Copyright 2008, 2009 Andrey Zholos. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the names of the copyright holders nor the names of contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
+154
View File
@@ -0,0 +1,154 @@
FastCGI C++ Class library (fcgicc)
1. Introduction
This is a simple C++ class library that provides FastCGI server functionality.
FastCGI is a protocol for connecting web servers with programs that generate
content. The protocol is described in more detail at http://www.fastcgi.com.
This library provides a single class which handles FastCGI connections on TCP/IP
or local domain sockets. Multiple connections are handled in a single thread
using select(). When a request is ready, it is passed to an application-
supplied callback for processing, after which the generated response is sent
back to the client.
2. Version information
This is the first release of fcgicc, version 0.1.2. It provides a full
implementation of the responder mode of FastCGI, but has undergone only limited
testing. Use with care!
3. Licensing
fcgicc is free software, available under a BSD-style license. There is no
warranty; not even for merchantability or fitness for a particular purpose. See
the file LICENSE.txt for complete information.
4. Installing
This library depends on the FastCGI Development Kit from http://www.fastcgi.com.
For convenience the required part of it is included in this distribution and
will be used if the development kit is not found on your system.
To build and install fcgicc as a standalone library you will need CMake, which
is available from http://cmake.org.
cd fcgicc
cmake .
make install
- or -
cmake -DPREFIX=$HOME/local .
make install
Alternatively, it may be simpler to import the two source files into your
project and build them as part of it.
5. Using
Here is how it works:
Client ------------> Web server ------> FastCGIServer ----------------.
HTTP request params FastCGIRequest |
in '
Application
.
|
Client <------------ Web server <------ FastCGIServer <---------------'
HTTP response out FastCGIRequest
err
The web server, which is a client to the FastCGI server, forwards a request as a
set of key-value parameter pairs and a standard input stream. The parameter
pairs are the environment variables from plain CGI, and they include such
important variables as REQUEST_URI. The standard input stream contains data
from POST requests.
An instance of the FastCGIServer class listens for requests from the web server,
builds a FastCGIRequest instance for each one, calls event handlers defined by
the application to process them, and responds to the web server.
The application processes requests using event handlers like this:
...
#include <fcgicc.h>
...
int handle_request(FastCGIRequest& request) {
// This is always the first event to occur. It occurs when the
// server receives all parameters. There may be more data coming on the
// standard input stream.
if (request.params.count("REQUEST_URI"))
return 0; // OK, continue processing
else
return 1; // Stop processing and return error code
}
int handle_data(FastCGIRequest& request) {
// This event occurs when data is received on the standard input stream.
// A simple string is used to hold the input stream, so it is the
// responsibility of the application to remember which data it has
// processed. The application may modify it; new data will be appended
// to it by the server. The same goes for the output and error streams:
// the application should append data to them; the server will remove
// all sent data from them.
std::transform(request.stdin.begin(), request.stdin.end(),
std::back_inserter(request.stderr),
std::bind1st(std::plus<char>(), 1));
request.stdin.clear(); // don't process it again
return 0; // still OK
}
class Application {
public:
int handle_complete(FastCGIRequest& request) {
// The event handler can also be a class member function. This
// event occurs when the parameters and standard input streams are
// both closed, and thus the request is complete.
request.out.append("Content-Type: text/plain\r\n\r\n");
request.out.append("You requested: ");
request.out.append(request.params[std::string("REQUEST_URI")]);
return 0;
}
};
...
The application sets up the FastCGI server like this:
...
FastCGIServer server; // Instantiate a server
// Set up our request handlers
server.request_handler(&handle_request);
server.data_handler(&handle_data);
server.complete_handler(application, &Application::handle_complete);
server.listen(7000); // Listen on a TCP port
server.listen(7001); // ... or on two
server.listen("./socket"); // ... and also on a local doman socket
server.process(100); // Process some data, but don't wait more
// than 100 ms for it to arrive.
server.process(); // Process some data with no timeout
server.process_forever(); // Process everything
...
6. Updates and feedback
This library is hosted at http://althenia.net/fcgicc. It is programmed by
Andrey Zholos <aaz@althenia.net>. Comments, bug reports and testing results are
welcome and will be appreciated.
+28
View File
@@ -0,0 +1,28 @@
This FastCGI application library source and object code (the
"Software") and its documentation (the "Documentation") are
copyrighted by Open Market, Inc ("Open Market"). The following terms
apply to all files associated with the Software and Documentation
unless explicitly disclaimed in individual files.
Open Market permits you to use, copy, modify, distribute, and license
this Software and the Documentation for any purpose, provided that
existing copyright notices are retained in all copies and that this
notice is included verbatim in any distributions. No written
agreement, license, or royalty fee is required for any of the
authorized uses. Modifications to this Software and Documentation may
be copyrighted by their authors and need not follow the licensing
terms described here. If modifications to this Software and
Documentation have new licensing terms, the new terms must be clearly
indicated on the first page of each file where they apply.
OPEN MARKET MAKES NO EXPRESS OR IMPLIED WARRANTY WITH RESPECT TO THE
SOFTWARE OR THE DOCUMENTATION, INCLUDING WITHOUT LIMITATION ANY
WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN
NO EVENT SHALL OPEN MARKET BE LIABLE TO YOU OR ANY THIRD PARTY FOR ANY
DAMAGES ARISING FROM OR RELATING TO THIS SOFTWARE OR THE
DOCUMENTATION, INCLUDING, WITHOUT LIMITATION, ANY INDIRECT, SPECIAL OR
CONSEQUENTIAL DAMAGES OR SIMILAR DAMAGES, INCLUDING LOST PROFITS OR
LOST DATA, EVEN IF OPEN MARKET HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES. THE SOFTWARE AND DOCUMENTATION ARE PROVIDED "AS IS".
OPEN MARKET HAS NO LIABILITY IN CONTRACT, TORT, NEGLIGENCE OR
OTHERWISE ARISING OUT OF THIS SOFTWARE OR THE DOCUMENTATION.
+136
View File
@@ -0,0 +1,136 @@
/*
* fastcgi.h --
*
* Defines for the FastCGI protocol.
*
*
* Copyright (c) 1995-1996 Open Market, Inc.
*
* See the file "LICENSE.TERMS" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
* $Id: fastcgi.h,v 1.1.1.1 1997/09/16 15:36:32 stanleyg Exp $
*/
#ifndef _FASTCGI_H
#define _FASTCGI_H
/*
* Listening socket file number
*/
#define FCGI_LISTENSOCK_FILENO 0
typedef struct {
unsigned char version;
unsigned char type;
unsigned char requestIdB1;
unsigned char requestIdB0;
unsigned char contentLengthB1;
unsigned char contentLengthB0;
unsigned char paddingLength;
unsigned char reserved;
} FCGI_Header;
#define FCGI_MAX_LENGTH 0xffff
/*
* Number of bytes in a FCGI_Header. Future versions of the protocol
* will not reduce this number.
*/
#define FCGI_HEADER_LEN 8
/*
* Value for version component of FCGI_Header
*/
#define FCGI_VERSION_1 1
/*
* Values for type component of FCGI_Header
*/
#define FCGI_BEGIN_REQUEST 1
#define FCGI_ABORT_REQUEST 2
#define FCGI_END_REQUEST 3
#define FCGI_PARAMS 4
#define FCGI_STDIN 5
#define FCGI_STDOUT 6
#define FCGI_STDERR 7
#define FCGI_DATA 8
#define FCGI_GET_VALUES 9
#define FCGI_GET_VALUES_RESULT 10
#define FCGI_UNKNOWN_TYPE 11
#define FCGI_MAXTYPE (FCGI_UNKNOWN_TYPE)
/*
* Value for requestId component of FCGI_Header
*/
#define FCGI_NULL_REQUEST_ID 0
typedef struct {
unsigned char roleB1;
unsigned char roleB0;
unsigned char flags;
unsigned char reserved[5];
} FCGI_BeginRequestBody;
typedef struct {
FCGI_Header header;
FCGI_BeginRequestBody body;
} FCGI_BeginRequestRecord;
/*
* Mask for flags component of FCGI_BeginRequestBody
*/
#define FCGI_KEEP_CONN 1
/*
* Values for role component of FCGI_BeginRequestBody
*/
#define FCGI_RESPONDER 1
#define FCGI_AUTHORIZER 2
#define FCGI_FILTER 3
typedef struct {
unsigned char appStatusB3;
unsigned char appStatusB2;
unsigned char appStatusB1;
unsigned char appStatusB0;
unsigned char protocolStatus;
unsigned char reserved[3];
} FCGI_EndRequestBody;
typedef struct {
FCGI_Header header;
FCGI_EndRequestBody body;
} FCGI_EndRequestRecord;
/*
* Values for protocolStatus component of FCGI_EndRequestBody
*/
#define FCGI_REQUEST_COMPLETE 0
#define FCGI_CANT_MPX_CONN 1
#define FCGI_OVERLOADED 2
#define FCGI_UNKNOWN_ROLE 3
/*
* Variable names for FCGI_GET_VALUES / FCGI_GET_VALUES_RESULT records
*/
#define FCGI_MAX_CONNS "FCGI_MAX_CONNS"
#define FCGI_MAX_REQS "FCGI_MAX_REQS"
#define FCGI_MPXS_CONNS "FCGI_MPXS_CONNS"
typedef struct {
unsigned char type;
unsigned char reserved[7];
} FCGI_UnknownTypeBody;
typedef struct {
FCGI_Header header;
FCGI_UnknownTypeBody body;
} FCGI_UnknownTypeRecord;
#endif /* _FASTCGI_H */
+3
View File
@@ -0,0 +1,3 @@
ADD_LIBRARY( fcgicc fcgicc.cc fcgicc.h )
INSTALL( FILES fcgicc.h DESTINATION include )
INSTALL( TARGETS fcgicc LIBRARY DESTINATION lib ARCHIVE DESTINATION lib )
+683
View File
@@ -0,0 +1,683 @@
/*
* Copyright 2008, 2009 Andrey Zholos. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the names of the copyright holders nor the names of contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* This file is part of the FastCGI C++ Class library (fcgicc) version 0.1,
* available at http://althenia.net/fcgicc
*/
#include "fcgicc.h"
#include <cstring>
#include <stdexcept>
#include <errno.h> // E*
#include <unistd.h> // read, write, close, unlink
#include <arpa/inet.h> // hton*
#include <netinet/in.h> // sockaddr_in, INADDR_*
#include <sys/select.h> // select, fd_set, FD_*, timeval
#include <sys/socket.h> // socket, bind, accept, listen, sockaddr, AF_*, SOCK_*
#include <sys/un.h> // sockaddr_un
#include "../fastcgi_devkit/fastcgi.h"
FastCGIServer::RequestInfo::RequestInfo() :
params_closed(false),
in_closed(false),
output_closed(false)
{
}
FastCGIServer::Connection::Connection() :
close_responsibility(false),
close_socket(false)
{
}
FastCGIServer::FastCGIServer()
{
}
FastCGIServer::~FastCGIServer()
{
if(my_pid != parent_pid) // if we're a child process, we must not close the handles
return;
for (std::vector<int>::iterator it = listen_sockets.begin();
it != listen_sockets.end(); ++it)
close(*it);
for (std::vector<std::string>::iterator it = listen_unlink.begin();
it != listen_unlink.end(); ++it)
unlink(it->c_str());
for (std::map<int, Connection*>::iterator it = read_sockets.begin();
it != read_sockets.end(); ++it) {
close(it->first);
for (RequestList::iterator req_it = it->second->requests.begin();
req_it != it->second->requests.end(); ++req_it)
delete req_it->second;
delete it->second;
}
}
void
FastCGIServer::listen(unsigned tcp_port)
{
int listen_socket = socket(PF_INET, SOCK_STREAM, 0);
if (listen_socket == -1)
throw std::runtime_error("socket() failed");
try {
struct sockaddr_in sa;
bzero(&sa, sizeof(sa));
sa.sin_family = AF_INET;
sa.sin_port = htons(tcp_port);
sa.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(listen_socket, (struct sockaddr*)&sa, sizeof(sa)) == -1)
throw std::runtime_error("bind() failed");
if (::listen(listen_socket, 100))
throw std::runtime_error("listen() failed");
listen_sockets.push_back(listen_socket);
} catch (...) {
close(listen_socket);
throw;
}
}
void
FastCGIServer::listen(const std::string& local_path)
{
int listen_socket = socket(PF_UNIX, SOCK_STREAM, 0);
if (listen_socket == -1)
throw std::runtime_error("socket() failed");
try {
struct sockaddr_un sa;
bzero(&sa, sizeof(sa));
sa.sun_family = AF_LOCAL;
std::string::size_type size = local_path.size();
if (size >= sizeof(sa.sun_path))
throw std::runtime_error("path too long");
if (local_path.find_first_of('\0') != std::string::npos)
throw std::runtime_error("null character in path");
std::memcpy(sa.sun_path, local_path.data(), size);
unlink(local_path.c_str());
try {
if (bind(listen_socket, (struct sockaddr*)&sa,
sizeof(sa) - (sizeof(sa.sun_path) - size - 1)) == -1)
throw std::runtime_error("bind() failed");
if (::listen(listen_socket, 100))
throw std::runtime_error("listen() failed");
listen_sockets.push_back(listen_socket);
listen_unlink.push_back(local_path);
} catch (...) {
unlink(local_path.c_str());
throw;
}
} catch (...) {
close(listen_socket);
throw;
}
}
void
FastCGIServer::abandon_files()
{
listen_unlink.clear();
}
void
FastCGIServer::process(int timeout_ms)
{
char buffer[4096];
fd_set fs_read;
fd_set fs_write;
int nfd = 0;
struct timeval tv = { timeout_ms / 1000, (timeout_ms % 1000) * 1000 };
FD_ZERO(&fs_read);
FD_ZERO(&fs_write);
for(auto socket_handle : listen_sockets)
{
FD_SET(socket_handle, &fs_read);
nfd = std::max(nfd, socket_handle);
}
for(auto con : read_sockets)
{
FD_SET(con.first, &fs_read);
if (!con.second->output_buffer.empty())
FD_SET(con.first, &fs_write);
nfd = std::max(nfd, con.first);
}
int select_result = select(nfd + 1, &fs_read, &fs_write, NULL,
timeout_ms < 0 ? NULL : &tv);
if (select_result == -1)
if (errno == EINTR)
return;
else
throw std::runtime_error("select() failed");
for(auto socket_handle : listen_sockets)
if (FD_ISSET(socket_handle, &fs_read))
{
int posix_con = accept(socket_handle, NULL, NULL);
if (posix_con == -1)
throw std::runtime_error("accept() failed");
read_sockets[posix_con] = new Connection();
read_sockets[posix_con]->posix_con = posix_con;
}
for (std::map<int, Connection*>::iterator it = read_sockets.begin();
it != read_sockets.end();)
{
int read_socket = it->first;
if (FD_ISSET(read_socket, &fs_read))
{
int read_result = read(read_socket, buffer, sizeof(buffer));
if (read_result == -1)
if (errno == ECONNRESET)
goto close_socket;
else
throw std::runtime_error("read() on socket failed");
if (read_result == 0)
it->second->close_socket = true;
else {
it->second->input_buffer.append(buffer, read_result);
process_connection_read(*it->second);
}
}
if (!it->second->output_buffer.empty() &&
FD_ISSET(read_socket, &fs_write))
{
process_connection_write(*it->second);
int write_result = write(read_socket,
it->second->output_buffer.data(),
it->second->output_buffer.size());
if (write_result == -1)
throw std::runtime_error("write() failed");
it->second->output_buffer.erase(0, write_result);
}
if (it->second->close_socket && it->second->output_buffer.empty())
{
close_socket:
int close_result = close(it->first);
if (close_result == -1 && errno != ECONNRESET)
throw std::runtime_error("close() failed");
Connection* connection = it->second;
read_sockets.erase(it++);
delete connection;
} else
++it;
}
}
void
FastCGIServer::process_forever()
{
for (;;)
process();
}
int
FastCGIServer::call_completion_handler(FastCGIRequest& request)
{
//printf("- request complete\n");
switch_to_arena(request.mem);
auto result = on_complete(request);
switch_to_system_alloc();
return(result);
}
void
FastCGIServer::process_connection_read(Connection& connection)
{
std::string::size_type n = 0;
while (connection.input_buffer.size() - n >= FCGI_HEADER_LEN) {
const FCGI_Header& header = *reinterpret_cast<const FCGI_Header*>(
connection.input_buffer.data() + n);
if (header.version != FCGI_VERSION_1) {
connection.close_socket = true;
break;
}
unsigned content_length =
(header.contentLengthB1 << 8) + header.contentLengthB0;
if (connection.input_buffer.size() - n <
FCGI_HEADER_LEN + content_length + header.paddingLength)
break;
const char* content =
connection.input_buffer.data() + n + FCGI_HEADER_LEN;
RequestID request_id = (header.requestIdB1 << 8) + header.requestIdB0;
switch (header.type)
{
case FCGI_GET_VALUES:
{
Pairs pairs = parse_pairs(content, content_length);
std::string::size_type base = connection.output_buffer.size();
connection.output_buffer.push_back(FCGI_VERSION_1);
connection.output_buffer.push_back(FCGI_GET_VALUES_RESULT);
connection.output_buffer.append(FCGI_HEADER_LEN - 2, 0);
for (Pairs::iterator it = pairs.begin(); it != pairs.end(); ++it)
if (it->first == FCGI_MAX_CONNS)
write_pair(connection.output_buffer,
it->first, std::string("100"));
else if (it->first == FCGI_MAX_REQS)
write_pair(connection.output_buffer,
it->first, std::string("1000"));
else if (it->first == FCGI_MPXS_CONNS)
write_pair(connection.output_buffer,
it->first, std::string("1"));
std::string::size_type len = connection.output_buffer.size() - base;
connection.output_buffer[base + 4] = (len >> 8) & 0xff;
connection.output_buffer[base + 5] = len & 0xff;
break;
}
case FCGI_BEGIN_REQUEST:
{
if (content_length < sizeof(FCGI_BeginRequestBody))
break;
const FCGI_BeginRequestBody& body =
*reinterpret_cast<const FCGI_BeginRequestBody*>(content);
if (!(body.flags & FCGI_KEEP_CONN))
connection.close_responsibility = true;
unsigned role = (body.roleB1 << 8) + body.roleB0;
if (role != FCGI_RESPONDER)
{
FCGI_EndRequestRecord unknown;
bzero(&unknown, sizeof(unknown));
unknown.header.version = FCGI_VERSION_1;
unknown.header.type = FCGI_END_REQUEST;
unknown.header.contentLengthB0 = sizeof(unknown.body);
unknown.body.protocolStatus = FCGI_UNKNOWN_ROLE;
connection.output_buffer.append(
reinterpret_cast<const char*>(&unknown), sizeof(unknown));
if (connection.close_responsibility)
connection.close_socket = true;
break;
}
{
RequestList::iterator it = connection.requests.find(request_id);
if (it != connection.requests.end())
{
//printf("- delete request object\n");
switch_to_arena(it->second->mem);
delete it->second;
switch_to_system_alloc();
connection.requests.erase(it);
}
}
if(connection.requests.size() > 1)
{
printf("(!) %i requests in flight at the same time!\n", connection.requests.size());
}
//auto arena = new MemoryArena(server_state.config.MAX_MEMORY, "request");
request_arena->clear();
switch_to_arena(request_arena);
RequestInfo* new_request = new RequestInfo();
new_request->resources.fcgi_socket = connection.posix_con;
new_request->mem = request_arena;
new_request->stats.time_init = microtime();
switch_to_system_alloc();
connection.requests[request_id] = new_request;
break;
}
case FCGI_ABORT_REQUEST:
{
RequestList::iterator it = connection.requests.find(request_id);
if (it == connection.requests.end())
break;
FCGI_EndRequestRecord aborted;
bzero(&aborted, sizeof(aborted));
aborted.header.version = FCGI_VERSION_1;
aborted.header.type = FCGI_END_REQUEST;
aborted.header.contentLengthB0 = sizeof(aborted.body);
aborted.body.appStatusB0 = 1;
aborted.body.protocolStatus = FCGI_REQUEST_COMPLETE;
connection.output_buffer.append(
reinterpret_cast<const char*>(&aborted), sizeof(aborted));
if (connection.close_responsibility)
connection.close_socket = true;
delete it->second;
connection.requests.erase(it);
break;
}
case FCGI_PARAMS:
{
RequestList::iterator it = connection.requests.find(request_id);
if (it == connection.requests.end())
break;
RequestInfo& request = *it->second;
switch_to_arena(it->second->mem);
if (!request.params_closed)
if (content_length != 0)
request.params_buffer.append(content, content_length);
else {
request.params = parse_pairs(request.params_buffer.data(),
request.params_buffer.size());
request.params_buffer.clear();
request.params_closed = true;
request.status = on_request(request);
if (request.status == 0 && !request.in.empty())
{
request.status = on_data(request);
if (request.status == 0 && request.in_closed)
request.status = call_completion_handler(request);
}
process_write_request(connection, request_id, request);
}
switch_to_system_alloc();
break;
}
case FCGI_STDIN:
{
RequestList::iterator it = connection.requests.find(request_id);
if (it == connection.requests.end())
break;
RequestInfo& request = *it->second;
switch_to_arena(it->second->mem);
if (!request.in_closed)
if (content_length != 0) {
request.in.append(content, content_length);
if (request.params_closed && request.status == 0)
{
request.status = on_data(request);
process_write_request(connection, request_id, request);
}
} else {
request.in_closed = true;
if (request.params_closed && request.status == 0) {
request.status = call_completion_handler(request);
process_write_request(connection, request_id, request);
}
}
switch_to_system_alloc();
break;
}
case FCGI_DATA:
break;
default:
{
FCGI_UnknownTypeRecord unknown;
bzero(&unknown, sizeof(unknown));
unknown.header.version = FCGI_VERSION_1;
unknown.header.type = FCGI_UNKNOWN_TYPE;
unknown.header.contentLengthB0 = sizeof(unknown.body);
unknown.body.type = header.type;
connection.output_buffer.append(
reinterpret_cast<const char*>(&unknown), sizeof(unknown));
}
}
n += FCGI_HEADER_LEN + content_length + header.paddingLength;
}
connection.input_buffer.erase(0, n);
}
void
FastCGIServer::process_write_request(Connection& connection, RequestID id,
RequestInfo& request)
{
if (!request.out.empty())
{
write_data(connection.output_buffer, id, request.out, FCGI_STDOUT);
switch_to_arena(request.mem);
request.out.clear();
switch_to_system_alloc();
}
if (!request.err.empty())
{
write_data(connection.output_buffer, id, request.err, FCGI_STDERR);
switch_to_arena(request.mem);
request.err.clear();
switch_to_system_alloc();
}
if ((request.in_closed || request.status != 0) &&
!request.output_closed)
{
switch_to_arena(request.mem);
request.out =
var_dump(request.header, "", "\r\n") +
var_dump(request.set_cookies, "", "\r\n") +
"\r\n";
for(auto obs : request.ob_stack)
{
request.out += obs->str();
delete obs;
}
request.ob_stack.clear();
switch_to_system_alloc();
write_data(connection.output_buffer, id, request.out, FCGI_STDOUT);
write_data(connection.output_buffer, id, request.err, FCGI_STDERR);
request.stats.time_end = microtime();
if(request.flags.log_request)
printf("(r) pid:%i\t%s\t%0.6fs\tfps:%0.0f\tout:%0.1fkB\tmem:%0.0f/%0.0fkB\n",
my_pid,
request.params["REQUEST_URI"].c_str(),
request.stats.time_end - request.stats.time_start,
1.0 / (request.stats.time_end - request.stats.time_start),
(f32)(request.out.length()/1024),
(f32)(request.mem->size/1024),
(f32)(request.mem->capacity/1024)
);
FCGI_EndRequestRecord complete;
bzero(&complete, sizeof(complete));
complete.header.version = FCGI_VERSION_1;
complete.header.type = FCGI_END_REQUEST;
complete.header.requestIdB1 = (id >> 8) & 0xff;
complete.header.requestIdB0 = id & 0xff;
complete.header.contentLengthB0 = sizeof(complete.body);
complete.body.appStatusB3 = (request.status >> 24) & 0xff;
complete.body.appStatusB2 = (request.status >> 16) & 0xff;
complete.body.appStatusB1 = (request.status >> 8) & 0xff;
complete.body.appStatusB0 = request.status & 0xff;
complete.body.protocolStatus = FCGI_REQUEST_COMPLETE;
connection.output_buffer.append(
reinterpret_cast<const char*>(&complete), sizeof(complete));
if (connection.close_responsibility)
connection.close_socket = true;
request.output_closed = true;
//printf("- output done\n");
}
switch_to_system_alloc();
}
void
FastCGIServer::process_connection_write(Connection& connection)
{
for (RequestList::iterator it = connection.requests.begin();
it != connection.requests.end();)
{
process_write_request(connection, it->first, *it->second);
if (it->second->params_closed && it->second->in_closed)
{
switch_to_arena(it->second->mem);
//printf("- process_connection_write close\n");
delete it->second;
switch_to_system_alloc();
connection.requests.erase(it++);
}
else
++it;
}
}
FastCGIServer::Pairs
FastCGIServer::parse_pairs(const char* data, std::string::size_type n)
{
Pairs pairs;
const unsigned char* u = reinterpret_cast<const unsigned char*>(data);
for (std::string::size_type m = 0; m < n;) {
std::string::size_type name_length, value_length;
if (u[m] >> 7) {
if (n - m < 4)
break;
name_length = ((u[m] & 0x7f) << 24) + (u[m + 1] << 16) +
(u[m + 2] << 8) + u[m + 3];
m += 4;
} else
name_length = u[m++];
if (m >= n)
break;
if (u[m] >> 7) {
if (n - m < 4)
break;
value_length = ((u[m] & 0x7f) << 24) + (u[m + 1] << 16) +
(u[m + 2] << 8) + u[m + 3];
m += 4;
} else
value_length = u[m++];
if (n - m < name_length)
break;
std::string key(data + m, name_length);
m += name_length;
if (n - m < value_length)
break;
pairs.insert(Pairs::value_type(
key, std::string(data + m, value_length)));
m += value_length;
}
return pairs;
}
void
FastCGIServer::write_pair(std::string& buffer,
const std::string& key, const std::string& value)
{
if (key.size() > 0x7f) {
buffer.push_back(0x80 + ((key.size() >> 24) & 0x7f));
buffer.push_back((key.size() >> 16) & 0xff);
buffer.push_back((key.size() >> 8) & 0xff);
buffer.push_back(key.size() & 0xff);
} else
buffer.push_back(key.size());
if (value.size() > 0x7f) {
buffer.push_back(0x80 + ((value.size() >> 24) & 0x7f));
buffer.push_back((value.size() >> 16) & 0xff);
buffer.push_back((value.size() >> 8) & 0xff);
buffer.push_back(value.size() & 0xff);
} else
buffer.push_back(value.size());
buffer.append(key);
buffer.append(value);
}
void
FastCGIServer::write_data(std::string& buffer, RequestID id,
const std::string& input, unsigned char type)
{
FCGI_Header header;
bzero(&header, sizeof(header));
header.version = FCGI_VERSION_1;
header.type = type;
header.requestIdB1 = (id >> 8) & 0xff;
header.requestIdB0 = id & 0xff;
for (std::string::size_type n = 0;;) {
std::string::size_type written = std::min(input.size() - n,
(std::string::size_type)0xffffu);
header.contentLengthB1 = written >> 8;
header.contentLengthB0 = written & 0xff;
header.paddingLength = (8 - (written % 8)) % 8;
buffer.append(
reinterpret_cast<const char*>(&header), sizeof(header));
buffer.append(input.data() + n, written);
buffer.append(header.paddingLength, 0);
n += written;
if (n == input.size())
break;
}
}
+105
View File
@@ -0,0 +1,105 @@
/*
* Copyright 2008, 2009 Andrey Zholos. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the names of the copyright holders nor the names of contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* This file is part of the FastCGI C++ Class library (fcgicc) version 0.1,
* available at http://althenia.net/fcgicc
*/
#ifndef FCGICC_H
#define FCGICC_H
#include <map>
#include <string>
#include <vector>
class FastCGIServer {
public:
FastCGIServer();
~FastCGIServer();
// called when the parameters and standard input have been receieved
std::function<int(FastCGIRequest&)> on_request = 0;
std::function<int(FastCGIRequest&)> on_data = 0;
std::function<int(FastCGIRequest&)> on_complete = 0;
void listen(unsigned tcp_port);
void listen(const std::string& local_path);
void abandon_files();
void process(int timeout_ms = -1); // timeout_ms<0 blocks forever
void process_forever();
int call_completion_handler(FastCGIRequest& request);
protected:
struct RequestInfo : FastCGIRequest {
RequestInfo();
std::string params_buffer;
bool params_closed;
bool in_closed;
int status;
bool output_closed;
friend class FastCGIServer;
};
typedef unsigned RequestID;
typedef std::map<RequestID, RequestInfo*> RequestList;
struct Connection {
Connection();
RequestList requests;
u64 posix_con = 0;
std::string input_buffer;
std::string output_buffer;
bool close_responsibility;
bool close_socket;
};
typedef StringMap Pairs;
std::vector<int> listen_sockets;
std::vector<std::string> listen_unlink;
std::map<int, Connection*> read_sockets;
void process_connection_read(Connection&);
static void process_write_request(Connection&, RequestID, RequestInfo&);
static void process_connection_write(Connection&);
static Pairs parse_pairs(const char*, std::string::size_type);
static void write_pair(std::string& buffer,
const std::string& key, const std::string&);
static void write_data(std::string& buffer, RequestID id,
const std::string& input, unsigned char type);
};
#endif // !FCGICC_H
+5
View File
@@ -0,0 +1,5 @@
ADD_EXECUTABLE( test1 test1.cc )
TARGET_LINK_LIBRARIES( test1 fcgicc )
ADD_EXECUTABLE( test2 test2.cc )
TARGET_LINK_LIBRARIES( test2 fcgicc )
INCLUDE_DIRECTORIES( ${PROJECT_SOURCE_DIR}/src )
+12
View File
@@ -0,0 +1,12 @@
server.bind = "127.0.0.1"
server.port = 8080
server.modules = ( "mod_fastcgi" )
server.document-root = "."
fastcgi.server = (
"/" => ((
"host" => "127.0.0.1",
"port" => 7000,
"check-local" => "disable"
))
)
+102
View File
@@ -0,0 +1,102 @@
/*
* Copyright 2008, 2009 Andrey Zholos. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the names of the copyright holders nor the names of contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* This file is part of the FastCGI C++ Class library (fcgicc) version 0.1,
* available at http://althenia.net/fcgicc
*/
#include <fcgicc.h>
#include <algorithm>
#include <functional>
int handle_request(FastCGIRequest& request) {
// This is always the first event to occur. It occurs when the
// server receives all parameters. There may be more data coming on the
// standard input stream.
if (request.params.count("REQUEST_URI"))
return 0; // OK, continue processing
else
return 1; // Stop processing and return error code
}
int handle_data(FastCGIRequest& request) {
// This event occurs when data is received on the standard input stream.
// A simple string is used to hold the input stream, so it is the
// responsibility of the application to remember which data it has
// processed. The application may modify it; new data will be appended
// to it by the server. The same goes for the output and error streams:
// the application should append data to them; the server will remove
// all sent data from them.
std::transform(request.in.begin(), request.in.end(),
std::back_inserter(request.err),
std::bind1st(std::plus<char>(), 1));
request.in.clear(); // don't process it again
return 0; // still OK
}
class Application {
public:
int handle_complete(FastCGIRequest& request) {
// The event handler can also be a class member function. This
// event occurs when the parameters and standard input streams are
// both closed, and thus the request is complete.
request.out.append("Content-Type: text/plain\r\n\r\n");
request.out.append("You requested: ");
request.out.append(request.params[std::string("REQUEST_URI")]);
return 0;
}
};
int main() {
Application application;
FastCGIServer server; // Instantiate a server
// Set up our request handlers
server.request_handler(&handle_request);
server.data_handler(&handle_data);
server.complete_handler(application, &Application::handle_complete);
server.listen(7000); // Listen on a TCP port
server.listen(7001); // ... or on two
server.listen("./socket"); // ... and also on a local doman socket
server.process(100); // Process some data, but don't wait more
// than 100 ms for it to arrive.
server.process(); // Process some data with no timeout
server.process_forever(); // Process everything
return 0;
}
+406
View File
@@ -0,0 +1,406 @@
/*
* Copyright 2008, 2009 Andrey Zholos. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the names of the copyright holders nor the names of contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* This file is part of the FastCGI C++ Class library (fcgicc) version 0.1,
* available at http://althenia.net/fcgicc
*/
/*
$ ./test2
This starts a simple FastCGI server on ports 7000-7009. It responds to HTTP
requests from a server such as lighttpd with the provided lighttpd.conf. It
also responds with a particular transformation of standard input.
$ ./test2 -c
This acts as a client by sending multiple concurrent data requests to the
handler on ports 7000 through 7009 and validates the responses.
*/
#include <fcgicc.h>
#include <algorithm>
#include <cstring>
#include <iostream>
#include <string>
#include <stdexcept>
#include <errno.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <fastcgi.h>
static const int base_port = 7000;
static const int processes = 25;
static const int requests = 1000;
static const std::string param_rot13("ROT13");
struct Rot13 : public std::unary_function<char, char> {
char operator() (char c) const {
if (c >= 'a' && c <= 'm' || c >= 'A' && c <= 'M')
return c + 13;
if (c >= 'n' && c <= 'z' || c >= 'N' && c <= 'Z')
return c - 13;
return c;
}
};
struct RandomChar {
char operator()() const {
return rand() % 256;
}
};
struct Handler {
int handle_request(FastCGIRequest& request)
{
static const std::string request_uri("REQUEST_URI");
FastCGIRequest::Params::const_iterator it =
request.params.find(request_uri);
if (it != request.params.end()) {
request.out.append("Content-Type: text/html\r\n\r\n"
"<html><body><h3>FastCGI C++ Class (fcgicc) test</h3>"
"<p>Request: ");
request.out.append(it->second);
request.out.append("</p></body></html>\n");
} else {
it = request.params.find(param_rot13);
if (it != request.params.end())
std::transform(it->second.begin(), it->second.end(),
std::back_inserter(request.out), Rot13());
}
return 0;
}
};
int handle_data(FastCGIRequest& request)
{
std::transform(request.in.begin(), request.in.end(),
std::back_inserter(request.out), Rot13());
request.in.clear();
return 0;
}
void server()
{
Handler handler;
FastCGIServer server;
server.request_handler(handler, &Handler::handle_request);
server.data_handler(&handle_data);
for (int i = 0; i < 10; i++)
server.listen(base_port + i);
server.process_forever();
}
struct Querier {
int socket;
void write(const char* data, size_t length) {
for (;;) {
int result = ::write(socket, data, length);
if (result == length)
return;
if (result <= 0)
throw std::runtime_error("write() failed in data client");
data += result;
length -= result;
}
}
bool read(std::string& output) {
char buf[4096];
int result = ::read(socket, buf, sizeof(buf));
if (result == -1)
throw std::runtime_error("read() failed in data client");
if (result == 0)
return false;
output.append(buf, result);
return true;
}
void write_stream(int type, const std::string& stream, size_t& i) {
size_t n = std::min(stream.size() - i, (std::string::size_type)65535);
if (n >= 256 || n > 1 && rand() % 3 != 0)
n = rand() % (n - 1) + 1;
size_t m = i == stream.size() || rand() % 3 != 0 ? 0 :
rand() % std::min(stream.size() - i, (std::string::size_type)63);
char padding[m];
bzero(padding, m);
FCGI_Header header;
bzero(&header, sizeof(header));
header.version = FCGI_VERSION_1;
header.type = type;
header.contentLengthB1 = n / 256;
header.contentLengthB0 = n % 256;
header.paddingLength = m;
write(reinterpret_cast<const char*>(&header), sizeof(header));
write(stream.data() + i, n);
write(padding, m);
i += n;
}
void encode_size(std::string& params, size_t n) {
if (n >> 7 == 0)
params.push_back(static_cast<char>(n));
else {
char c[4];
c[0] = static_cast<char>(n >> 24 | 0x80);
c[1] = static_cast<char>(n >> 16);
c[2] = static_cast<char>(n >> 8);
c[3] = static_cast<char>(n);
params.append(c, 4);
}
}
void random_params(std::string& params) {
int i = rand() % 15;
if (i > 10)
i = 0;
for (; i >= 0; i--) {
size_t m = rand() % 300, n = rand() % 700;
encode_size(params, m + 1);
encode_size(params, n);
params.push_back('_');
std::generate_n(std::back_inserter(params), m + n, RandomChar());
}
}
void process() {
for (int i = 0; i < requests; i++) {
// Generate random request and send it either as a special parameter
// or as the standard input stream. Pad with random parameters.
std::string params, in, request;
std::generate_n(std::back_inserter(request),
rand() % 10000, RandomChar());
random_params(params);
if (rand() % 3 == 0)
in.append(request);
else {
encode_size(params, param_rot13.size());
encode_size(params, request.size());
params.append(param_rot13);
params.append(request);
}
random_params(params);
// Connect to the server
socket = ::socket(PF_INET, SOCK_STREAM, 0);
if (socket == -1)
throw std::runtime_error("socket() failed in data client");
struct sockaddr_in sa;
bzero(&sa, sizeof(sa));
sa.sin_family = AF_INET;
sa.sin_port = htons(base_port + rand() % 10);
sa.sin_addr.s_addr = htonl(0x7f000001);
if (::connect(socket, (struct sockaddr*)&sa, sizeof(sa)) == -1)
throw std::runtime_error("connect() failed in data client");
static FCGI_BeginRequestRecord begin;
bzero(&begin, sizeof(begin));
begin.header.version = FCGI_VERSION_1;
begin.header.type = FCGI_BEGIN_REQUEST;
begin.header.contentLengthB0 = sizeof(begin.body);
begin.body.roleB0 = FCGI_RESPONDER;
if (rand() % 3 == 0)
begin.body.flags = FCGI_KEEP_CONN;
write(reinterpret_cast<const char*>(&begin), sizeof(begin));
// Send streams in random chunks
size_t in_i = 0, params_i = 0;
while (in_i < in.size() || params_i < params.size()) {
if (in_i == in.size() ||
params_i < params.size() && rand() % 3 == 0)
write_stream(FCGI_PARAMS, params, params_i);
else
write_stream(FCGI_STDIN, in, in_i);
}
if (rand() % 3 == 0) {
write_stream(FCGI_PARAMS, params, params_i);
write_stream(FCGI_STDIN, in, in_i);
} else {
write_stream(FCGI_STDIN, in, in_i);
write_stream(FCGI_PARAMS, params, params_i);
}
// Sometimes close our end of the socket
if (rand() % 5 == 0)
if (::shutdown(socket, SHUT_WR) == -1)
throw std::runtime_error("shutdown() failed "
"in data client");
// Receive and verify results
std::string output;
std::string out, err;
bool closed_out = false, closed_err = false;
while (read(output)) {
another_record:
if (output.size() < sizeof(FCGI_Header))
continue;
const FCGI_Header& header =
*reinterpret_cast<const FCGI_Header*>(output.data());
if (header.version != FCGI_VERSION_1)
throw std::runtime_error("received: incorrect version");
size_t content = (header.contentLengthB1 << 8) +
header.contentLengthB0;
size_t padding = header.paddingLength;
if (output.size() - sizeof(FCGI_Header) < content + padding)
continue;
switch (header.type) {
case FCGI_STDOUT:
if (closed_out)
throw std::runtime_error("received: "
"data on closed stream");
out.append(output.data() + sizeof(FCGI_Header), content);
if (content == 0)
closed_out = true;
break;
case FCGI_STDERR:
if (closed_err)
throw std::runtime_error("received: "
"data on closed stream");
err.append(output.data() + sizeof(FCGI_Header), content);
if (content == 0)
closed_err = true;
break;
case FCGI_END_REQUEST: {
if (!closed_out || !closed_err)
throw std::runtime_error("received: "
"streams not closed");
if (output.size() - sizeof(FCGI_Header) <
sizeof(FCGI_EndRequestBody))
continue;
const FCGI_EndRequestBody& body =
*reinterpret_cast<const FCGI_EndRequestBody*>(
output.data() + sizeof(FCGI_Header));
if ((body.appStatusB3 | body.appStatusB2 |
body.appStatusB1 | body.appStatusB0) != 0)
throw std::runtime_error("received: bad exit code");
if (body.protocolStatus != FCGI_REQUEST_COMPLETE)
throw std::runtime_error("received: bad status");
if (content + padding !=
output.size() - sizeof(FCGI_Header))
throw std::runtime_error("received: extra data");
goto request_complete;
}
default:
throw std::runtime_error("received: unexpected type");
}
output.erase(0, sizeof(FCGI_Header) + content + padding);
goto another_record;
}
throw std::runtime_error("received: not enough data");
request_complete:
if (::close(socket) == -1 && errno != ECONNRESET)
throw std::runtime_error("close() failed in data client");
if (!err.empty())
throw std::runtime_error("received: incorrect stderr");
std::string result;
std::transform(out.begin(), out.end(),
std::back_inserter(result), Rot13());
if (result != request)
throw std::runtime_error("received: incorrect stdout");
}
}
};
void client()
{
for (int i = 0; i < processes; i++) {
switch (::fork()) {
case -1:
throw std::runtime_error("fork() failed");
case 0:
::srand(i);
Querier querier;
querier.process();
return;
}
}
for (int i = 0; i < processes; i++) {
int status;
if (::wait(&status) == -1)
throw std::runtime_error("wait() failed");
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
throw std::runtime_error("data client failed");
}
std::cout << "Success!\n";
}
int main(int argc, const char* argv[])
{
try {
static const std::string arg_client("-c");
for (int i = 1; i < argc; i++)
if (argv[i] == arg_client) {
client();
return 0;
}
server();
return 0;
} catch (std::exception& e) {
std::cerr << "Error: " << e.what() << ".\n";
} catch (...) {
std::cerr << "Error.\n";
}
return 1;
}
+325
View File
@@ -0,0 +1,325 @@
#include "compiler.h"
#include <sys/file.h>
String process_html_literal(Request* context, SharedUnit* su, String content)
{
String pc;
String HT_START = "print(R\"(";
String HT_END = ")\");";
u8 mode = 0;
char quote_char;
bool inside_quote = false;
String code_buffer = "";
bool is_field = false;
for(u32 i = 0; i < content.length(); i++)
{
char c = content[i];
switch(mode)
{
case(0):
if(c == '<' && content[i+1] == '?')
{
code_buffer = "";
if(content[i+2] == '=')
{
is_field = true;
i += 2;
}
else
{
is_field = false;
i += 1;
}
mode = 1; // code-parsing mode
}
else
{
pc.append(1, c);
}
break;
case(1):
if(inside_quote)
{
if(quote_char == c && content[i-1] != '\\')
inside_quote = false;
code_buffer.append(1, c);
}
else
{
if(c == '\"' || c == '\'')
{
inside_quote = true;
quote_char = c;
code_buffer.append(1, c);
}
else if(c == '?' && content[i+1] == '>')
{
mode = 0;
i += 1;
if(is_field)
{
pc.append(
HT_END +
"print(html_escape( " +
code_buffer +
" )); " +
HT_START
);
}
else
{
pc.append(HT_END + code_buffer + HT_START);
}
}
else
{
code_buffer.append(1, c);
}
}
break;
}
}
return(HT_START + pc + HT_END);
}
String preprocess_shared_unit(Request* context, SharedUnit* su)
{
String content = file_get_contents(su->file_name);
printf("(c) compiling with root dir %s\n", context->server->config.COMPILER_SYS_PATH.c_str());
String pc = ("#include \"")+context->server->config.COMPILER_SYS_PATH +"/src/lib/uce_lib.h\" \n";
String token = "";
String html_buffer = "";
u8 mode = 0;
bool inside_quote = false;
for(u32 i = 0; i < content.length(); i++)
{
char c = content[i];
if(mode == 2)
{
auto end_pos = content.find(String("</")+token+">", i);
if(end_pos != String::npos)
{
u32 len = token.length() + 3 + end_pos - i;
html_buffer.append(content.substr(i, len - (token.length() == 0 ? 3 : 0)));
i += len - 1;
pc.append(process_html_literal(context, su, html_buffer));
}
else
{
printf("(!) unterminated HTML literal <%s> in %s", token.c_str(), su->file_name.c_str());
}
mode = 0;
}
else if(mode == 1)
{
if(isspace(c) || c == '>')
{
mode = 2;
if(token.length() > 0)
html_buffer.append(1, c);
}
else
{
token.append(1, c);
html_buffer.append(1, c);
}
}
else if(!inside_quote && c == '<' && (content[i+1] == '>'/* || isalpha(content[i+1])*/))
{
mode = 1;
token = "";
html_buffer = "";
if(content[i+1] != '>')
html_buffer.append(1, c);
}
else if(c == '\"')
{
inside_quote = !inside_quote;
pc.append(1, c);
}
else
{
pc.append(1, c);
}
}
return(pc);
}
void setup_unit_paths(Request* context, SharedUnit* su, String file_name)
{
su->file_name = file_name;
if(su->src_path.length() > 0) // we did this already
return;
su->src_path = dirname(file_name);
su->bin_path = context->server->config.BIN_DIRECTORY + su->src_path;
su->pre_path = context->server->config.BIN_DIRECTORY + su->src_path;
su->src_file_name = basename(file_name);
su->bin_file_name = su->src_file_name + ".so";
su->pre_file_name = su->src_file_name + ".cpp";
su->so_name = su->bin_path + "/" + su->bin_file_name;
}
void load_shared_unit(Request* context, SharedUnit* su, String file_name)
{
//setup_unit_paths(context, su, file_name);
su->on_render = 0;
su->on_setup = 0;
su->compiler_messages = "";
if(!file_exists(su->so_name))
{
printf("(i) unit file not found: %s\n", su->so_name.c_str());
su->compiler_messages = "unit file not found";
return;
}
su->so_handle = dlopen(su->so_name.c_str(), RTLD_NOW);
if(su->so_handle)
{
su->last_compiled = file_mtime(su->so_name);
char *error;
su->on_setup = (request_handler)dlsym(su->so_handle, "set_current_request");
su->on_render = (call_handler)dlsym(su->so_handle, "render");
if ((error = dlerror()) != NULL)
printf("Error - %s in %s\n", error, su->file_name.c_str());
else
printf("(i) loaded unit %s\n", su->file_name.c_str());
}
else
{
printf("Error loading unit %s, could not open %s\n", su->file_name.c_str(), su->so_name.c_str());
}
}
void compile_shared_unit(Request* context, SharedUnit* su, String file_name)
{
//setup_unit_paths(context, su, file_name);
if(!file_exists(su->file_name))
{
su->compiler_messages = "source file not found (" + su->file_name + ")";
return;
}
shell_exec("mkdir -p " + shell_escape(su->pre_path));
file_put_contents(su->pre_path + "/" + su->pre_file_name, preprocess_shared_unit(context, su));
printf("Config.COMPILE_SCRIPT %s\n", String(su->pre_path + "/" + su->pre_file_name).c_str());
su->compiler_messages = trim(shell_exec(context->server->config.COMPILE_SCRIPT+" "+
shell_escape(su->src_path)+" "+
shell_escape(su->bin_path)+" "+
shell_escape(su->file_name)+" "+
shell_escape(su->pre_file_name)+" "+
shell_escape(su->bin_file_name)
));
if(su->compiler_messages.length() > 0)
{
printf("%s \n", su->compiler_messages.c_str());
}
else
{
load_shared_unit(context, su, file_name);
printf("(i) compiled unit %s\n", file_name.c_str());
}
}
SharedUnit* get_shared_unit(Request* context, String file_name)
{
SharedUnit* su = context->server->units[file_name];
auto mod_time = file_mtime(file_name);
bool do_recompile = false;
if(su && (su->last_compiled < mod_time || mod_time == 0))
{
delete su;
su = 0;
do_recompile = true;
}
if(!su)
{
su = new SharedUnit();
setup_unit_paths(context, su, file_name);
if(!do_recompile)
{
// if we didn't decide to force recompile yet, we need to check
// (this case should only happen if the SU cache for that entry is cold
// but the SO itself exists _AND_ is stale)
su->last_compiled = file_mtime(su->so_name);
if(su->last_compiled < mod_time || mod_time == 0)
do_recompile = true;
}
int fdlock = open((su->so_name+".lock").c_str(), O_RDWR | O_CREAT, 0666 );
int fl_excl = flock(fdlock, LOCK_EX);
if(do_recompile)
{
compile_shared_unit(context, su, file_name);
}
else
{
load_shared_unit(context, su, file_name);
if(!su->so_handle)
compile_shared_unit(context, su, file_name);
}
flock(fdlock, LOCK_UN);
close(fdlock);
remove((su->so_name+".lock").c_str());
context->server->units[file_name] = su;
}
return(su);
}
void compiler_invoke(Request* context, String file_name, DTree& call_param)
{
if(file_name[0] != '/')
{
file_name = expand_path(file_name);
}
//printf("(i) invoke %s\n", file_name.c_str());
//printf("(i) invoke(%s)\n", file_name.c_str());
switch_to_system_alloc();
auto su = get_shared_unit(context, file_name);
switch_to_arena(context->mem);
if(!su)
{
printf("Error loading unit %s\n", file_name.c_str());
print("Error loading unit: "+file_name);
}
else if(!su->on_render)
{
context->header["Content-Type"] = "text/plain";
print("Compiler error: "+su->compiler_messages);
}
else
{
if(su->compiler_messages.length() > 0)
print(su->compiler_messages);
else
{
String prev_wd = get_cwd();
set_cwd(su->src_path);
su->on_setup(context);
su->on_render(call_param);
set_cwd(prev_wd);
}
}
}
+10
View File
@@ -0,0 +1,10 @@
#define RENDER() extern "C" void render(DTree& call)
String process_html_literal(Request* context, SharedUnit* su, String content);
String preprocess_shared_unit(Request* context, SharedUnit* su);
void setup_unit_paths(Request* context, SharedUnit* su, String file_name);
void load_shared_unit(Request* context, SharedUnit* su, String file_name);
void compile_shared_unit(Request* context, SharedUnit* su, String file_name);
SharedUnit* get_shared_unit(Request* context, String file_name);
void compiler_invoke(Request* context, String file_name, DTree& call_param);
+263
View File
@@ -0,0 +1,263 @@
void DTree::each(std::function <void (DTree t, String key)> f)
{
switch(type)
{
case('M'):
for (auto it = _map.begin(); it != _map.end(); ++it)
{
f(it->second, it->first);
}
break;
default:
f(*this, "");
break;
}
}
bool DTree::is_array()
{
return(type == 'M');
}
String DTree::to_string()
{
switch(type)
{
case('S'):
return(_String);
break;
case('F'):
return(std::to_string(_float));
break;
case('B'):
return(_bool ? "(true)" : "(false)");
break;
case('M'):
return("");
break;
case('P'):
return(std::to_string((u64)_ptr));
break;
}
}
String DTree::to_json()
{
switch(type)
{
case('S'):
return(json_escape(_String));
break;
case('F'):
return(std::to_string(_float));
break;
case('B'):
return(_bool ? "true" : "false");
break;
case('M'):
return("\"(array)\"");
break;
case('P'):
return("\"(pointer)\"");
break;
}
}
String DTree::get_type_name()
{
switch(type)
{
case('S'):
return("String");
break;
case('F'):
return("f64");
break;
case('B'):
return("bool");
break;
case('M'):
return("array");
break;
case('P'):
return("pointer");
break;
}
}
void DTree::set_type(char t)
{
if(type != t)
{
type = t;
switch(type)
{
case('M'):
_map.clear();
_array_index = 0;
break;
}
}
}
void DTree::set(String s)
{
set_type('S');
_String = s;
}
void DTree::set(void* p)
{
set_type('P');
_ptr = p;
}
void DTree::set(f64 f)
{
set_type('F');
_float = f;
}
void DTree::set_bool(bool b)
{
set_type('B');
_bool = b;
}
void DTree::set(DTree source)
{
set_type(source.type);
switch(type)
{
case('S'):
_String = source._String;
break;
case('F'):
_float = source._float;
break;
case('B'):
_bool = source._bool;
break;
case('M'):
_map = source._map;
break;
case('P'):
_ptr = source._ptr;
break;
}
}
void DTree::set(StringMap source)
{
set_type('M');
for (auto it = source.begin(); it != source.end(); ++it)
{
_map[it->first] = it->second;
}
}
DTree* DTree::key(String s)
{
set_type('M');
return(&_map[s]);
}
DTree& DTree::operator [] (String s) {
set_type('M');
return(_map[s]);
}
void DTree::operator = (String v) { set(v); }
void DTree::operator = (f64 v) { set(v); }
void DTree::operator = (void* v) { set(v); }
void DTree::operator = (DTree v) { set(v); }
void DTree::operator = (StringMap v) { set(v); }
void DTree::push(DTree& child)
{
set_type('M');
_map[std::to_string(_array_index)] = child;
_array_index += 1;
}
DTree DTree::pop()
{
set_type('M');
auto last = _map.rbegin();
DTree result = last->second;
_map.erase(last->first);
return(result);
}
void DTree::remove(String s)
{
set_type('M');
_map.erase(s);
}
void DTree::clear()
{
set_type('M');
_map.clear();
}
String to_String(DTree t)
{
return(t.to_string());
}
String var_dump(DTree map, String prefix, String postfix)
{
String result = "";
if(!map.is_array())
return(map.to_string());
map.each([&] (DTree item, String key) {
result += prefix + key + ": " + item.to_string() + postfix;
if(item.is_array())
result += var_dump(item, prefix + "\t");
});
return(result);
}
String json_escape(String s)
{
//return(String("\"")+s+"\"");
String result;
u32 i = 0;
result.append(1, '"');
while(i < s.length())
{
char c = s[i];
switch(c)
{
case('\t'):
result.append("\\t");
break;
case('\n'):
result.append("\\n");
break;
case('"'):
result.append("\\\"");
break;
case('\r'):
result.append("\\r");
break;
case('\\'):
result.append("\\\\");
break;
case('\b'):
result.append("\\b");
break;
case('\f'):
result.append("\\f");
break;
default:
result.append(1, c);
break;
}
i += 1;
}
result.append(1, '"');
return(result);
}
+41
View File
@@ -0,0 +1,41 @@
String json_escape(String s);
struct DTree {
char type = 'S';
String _String;
f64 _float;
s64 _array_index;
bool _bool;
void* _ptr;
std::map<String, DTree> _map;
void each(std::function <void (DTree t, String key)> f);
bool is_array();
String to_string();
String to_json();
String get_type_name();
void set_type(char t);
void set(String s);
void set(void* p);
void set(f64 f);
void set_bool(bool b);
void set(DTree source);
void set(StringMap source);
DTree* key(String s);
DTree& operator [] (String s);
void operator = (String v);
void operator = (f64 v);
void operator = (void* v);
void operator = (DTree v);
void operator = (StringMap v);
void push(DTree& child);
DTree pop();
void remove(String s);
void clear();
};
String to_String(DTree t);
String var_dump(DTree map, String prefix = "", String postfix = "\n");
+412
View File
@@ -0,0 +1,412 @@
#include "functionlib.h"
String var_dump(StringMap map, String prefix, String postfix)
{
String result = "";
for (auto it = map.begin(); it != map.end(); ++it)
{
result.append(prefix + it->first + ": " + it->second + postfix);
}
return(result);
}
String var_dump(StringList slist, String prefix, String postfix)
{
String result = "";
for (auto& s : slist)
{
result.append(prefix + s + postfix);
}
return(result);
}
u8 char_to_u8(char input)
{
if(input >= '0' && input <= '9')
return input - '0';
if(input >= 'A' && input <= 'F')
return input - 'A' + 10;
if(input >= 'a' && input <= 'f')
return input - 'a' + 10;
return(0);
}
u8 hex_to_u8(String src)
{
return(char_to_u8(src[0])*16 + char_to_u8(src[1]));
}
String str_to_lower(String s)
{
String result = "";
for(auto c : s)
{
if(c >= 'A' && c <= 'Z')
c = tolower(c);
result.append(1, c);
}
return(result);
}
String str_to_upper(String s)
{
String result = "";
for(auto c : s)
{
if(c >= 'A' && c <= 'Z')
c = toupper(c);
result.append(1, c);
}
return(result);
}
String trim(String raw)
{
u32 len = raw.length();
u32 start_pos = 0;
u32 end_pos = len - 1;
if(len == 0 || (len == 1 && isspace(raw[0])))
return("");
while(start_pos < len && isspace(raw[start_pos]))
start_pos++;
while(end_pos >= 0 && isspace(raw[end_pos]))
end_pos--;
if(end_pos < start_pos)
return("");
return(raw.substr(start_pos, 1 + end_pos - start_pos));
}
StringList split(String str, String delim)
{
StringList result;
int start = 0;
int end = str.find(delim);
while (end != String::npos)
{
result.push_back(str.substr(start, end - start));
start = end + delim.size();
end = str.find(delim, start);
}
result.push_back(str.substr(start, end - start));
return(result);
}
String join(StringList l, String delim)
{
String result;
u32 i = 0;
for(auto& s : l)
{
if(i > 0)
result.append(delim);
result.append(s);
i += 1;
}
return(result);
}
String html_escape(String s)
{
String result;
for(u32 i = 0; i < s.length(); i++)
{
char c = s[i];
switch(c)
{
case('&'):
result.append("&amp;");
break;
case('<'):
result.append("&lt;");
break;
case('>'):
result.append("&gt;");
break;
case('"'):
result.append("&quot;");
break;
default:
result.append(1, c);
break;
}
}
return(result);
}
String html_escape(u64 a)
{
return(std::to_string(a));
}
String html_escape(f64 a)
{
return(std::to_string(a));
}
u64 int_val(String s, u32 base)
{
return(strtoll(s.c_str(), 0, base));
}
String nibble(String& haystack, String delim)
{
auto idx = haystack.find(delim);
if(idx == String::npos)
{
String result = haystack;
haystack = "";
return(result);
}
else
{
String result = haystack.substr(0, idx);
haystack = haystack.substr(idx+delim.length());
return(result);
}
}
String json_encode(DTree t)
{
String result = "";
if(t.is_array())
{
result += "{";
u32 count = 0;
t.each([&] (DTree item, String key) {
if(count > 0)
result += ", ";
count += 1;
result += json_escape(key) + ": " + json_encode(item);
});
result += "}";
}
else
{
result = t.to_json();
}
return(result);
}
// https://i.stack.imgur.com/SHLOB.gif
String json_decode_String(String s, u32& i, char termination_char)
{
String result;
//print("json_decode_String " + s.substr(i) + "\n");
while(i < s.length())
{
char c = s[i];
if(c == termination_char)
{
i += 1;
//print("json_decode_String = " + result + "\n");
return(result);
}
else if(c == '\\')
{
i += 1;
c = s[i];
switch(c)
{
case('t'):
result.append(1, '\t');
break;
case('n'):
result.append(1, '\n');
break;
case('r'):
result.append(1, '\r');
break;
case('\\'):
result.append(1, '\\');
break;
case('b'):
result.append(1, '\b');
break;
case('f'):
result.append(1, '\f');
break;
case('u'):
// todo decode
break;
default:
result.append(1, c);
break;
}
}
else
{
result.append(1, c);
}
i += 1;
}
return(result);
}
DTree json_decode_map(String s, u32& i);
void json_consume_space(String s, u32& i)
{
while(i < s.length() && isspace(s[i]))
i += 1;
}
String json_decode_keyword(String s, u32& i)
{
String result;
json_consume_space(s, i);
while(i < s.length())
{
char c = s[i];
if(isalnum(c))
{
result.append(1, c);
}
else
{
return(result);
}
i += 1;
}
return(result);
}
String json_decode_number(String s, u32& i)
{
String result;
json_consume_space(s, i);
while(i < s.length())
{
char c = s[i];
if(isdigit(c) || c == '.')
{
result.append(1, c);
}
else
{
return(result);
}
i += 1;
}
return(result);
}
DTree json_decode_value(String s, u32& i)
{
DTree result;
String value = "";
json_consume_space(s, i);
char c = s[i];
//print("json_decode_value " + s.substr(i) + "\n");
if(c == '"' || c == '\'') // String value
{
result.type = 'S';
i += 1;
result._String = json_decode_String(s, i, s[i-1]);
return(result);
}
else if(isdigit(c))
{
result.type = 'S';
result._String = json_decode_number(s, i);
//result._float = stod(json_decode_number(s, i));
return(result);
}
else if(c == '{')
{
i += 1;
return(json_decode_map(s, i));
}
else
{
value = json_decode_keyword(s, i);
if(value == "true")
result.set_bool(true);
else if(value == "false")
result.set_bool(false);
else if(value == "null")
result.set("");
return(result);
}
return(result);
}
DTree json_decode_map(String s, u32& i)
{
DTree result;
result.type = 'M';
String key = "";
json_consume_space(s, i);
//print("json_decode_map " + s.substr(i) + "\n");
while(i < s.length())
{
char c = s[i];
if(c == '}')
{
i += 1;
return(result);
}
else if(c == ',')
{
i += 1;
}
else if(c == '"' || c == '\'')
{
i += 1;
key = json_decode_String(s, i, s[i-1]);
json_consume_space(s, i);
if(s[i] != ':')
return(result); // malformed
i += 1;
DTree v = json_decode_value(s, i);
//result._map[key] = json_decode_value(s, i);
//print("KV " + key + " = " + to_String(v) + "\n");
//printf("map add %s (%c) \n", key.c_str(), s[i]);
result._map[key] = v;
}
else
{
// malformed
return(result);
}
json_consume_space(s, i);
}
return(result);
}
DTree json_decode(String s)
{
u32 i = 0;
return(json_decode_value(s, i));
}
void ob_start()
{
context->ob_start();
}
void ob_close()
{
delete context->ob;
context->ob_stack.pop_back();
if(context->ob_stack.size() == 0)
ob_start();
}
String ob_get()
{
return(context->ob->str());
}
String ob_get_close()
{
String result = context->ob->str();
ob_close();
return(result);
}
+50
View File
@@ -0,0 +1,50 @@
u8 char_to_u8(char input);
u8 hex_to_u8(String src);
u64 int_val(String s, u32 base = 10);
String str_to_lower(String s);
String str_to_upper(String s);
String trim(String raw);
StringList split(String str, String delim = "\n");
String join(StringList l, String delim = "\n");
String nibble(String& haystack, String delim);
void json_consume_space(String s, u32& i);
template<typename T>
std::vector<T> filter(std::vector<T> items, std::function<bool (T)> f)
{
std::vector<T> new_items;
for(auto item : items)
{
if(f(item))
new_items.push_back(item);
}
return(new_items);
}
template <class ...Args>
String first(Args... args)
{
std::vector<String> vec = {args...};
for(auto s : vec)
if(trim(s) != "")
return(s);
return("");
}
String html_escape(String s);
String html_escape(u64 a);
String html_escape(f64 a);
String json_encode(DTree t);
DTree json_decode(String s);
String var_dump(StringMap map, String prefix = "", String postfix = "\n");
String var_dump(StringList slist, String prefix = "", String postfix = "\n");
void ob_start();
void ob_clear();
String ob_get_clear();
String ob_get();
+341
View File
@@ -0,0 +1,341 @@
/* from valgrind tests */
/* ================ sha1.c ================ */
/*
SHA-1 in C
By Steve Reid <steve@edmweb.com>
100% Public Domain
Test Vectors (from FIPS PUB 180-1)
"abc"
A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
A million repetitions of "a"
34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
*/
/* #define LITTLE_ENDIAN * This should be #define'd already, if true. */
/* #define SHA1HANDSOFF * Copies data before messing with it. */
typedef struct {
u_int32_t state[5];
u_int32_t count[2];
unsigned char buffer[64];
} SHA1_CTX;
void SHA1Transform(u_int32_t state[5], const unsigned char buffer[64]);
void SHA1Init(SHA1_CTX* context);
void SHA1Update(SHA1_CTX* context, const unsigned char* data, u_int32_t len);
void SHA1Final(unsigned char digest[20], SHA1_CTX* context);
#define SHA1HANDSOFF
#include <stdio.h>
#include <string.h>
#include <sys/types.h> /* for u_int*_t */
#include "hash.h"
#ifndef BYTE_ORDER
#if (BSD >= 199103)
# include <machine/endian.h>
#else
#if defined(linux) || defined(__linux__)
# include <endian.h>
#else
#define LITTLE_ENDIAN 1234 /* least-significant byte first (vax, pc) */
#define BIG_ENDIAN 4321 /* most-significant byte first (IBM, net) */
#define PDP_ENDIAN 3412 /* LSB first in word, MSW first in long (pdp)*/
#if defined(vax) || defined(ns32000) || defined(sun386) || defined(__i386__) || \
defined(MIPSEL) || defined(_MIPSEL) || defined(BIT_ZERO_ON_RIGHT) || \
defined(__alpha__) || defined(__alpha)
#define BYTE_ORDER LITTLE_ENDIAN
#endif
#if defined(sel) || defined(pyr) || defined(mc68000) || defined(sparc) || \
defined(is68k) || defined(tahoe) || defined(ibm032) || defined(ibm370) || \
defined(MIPSEB) || defined(_MIPSEB) || defined(_IBMR2) || defined(DGUX) ||\
defined(apollo) || defined(__convex__) || defined(_CRAY) || \
defined(__hppa) || defined(__hp9000) || \
defined(__hp9000s300) || defined(__hp9000s700) || \
defined (BIT_ZERO_ON_LEFT) || defined(m68k) || defined(__sparc)
#define BYTE_ORDER BIG_ENDIAN
#endif
#endif /* linux */
#endif /* BSD */
#endif /* BYTE_ORDER */
#if defined(__BYTE_ORDER) && !defined(BYTE_ORDER)
#if (__BYTE_ORDER == __LITTLE_ENDIAN)
#define BYTE_ORDER LITTLE_ENDIAN
#else
#define BYTE_ORDER BIG_ENDIAN
#endif
#endif
#if !defined(BYTE_ORDER) || \
(BYTE_ORDER != BIG_ENDIAN && BYTE_ORDER != LITTLE_ENDIAN && \
BYTE_ORDER != PDP_ENDIAN)
/* you must determine what the correct bit order is for
* your compiler - the next line is an intentional error
* which will force your compiles to bomb until you fix
* the above macros.
*/
#error "Undefined or invalid BYTE_ORDER"
#endif
#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
/* blk0() and blk() perform the initial expand. */
/* I got the idea of expanding during the round function from SSLeay */
#if BYTE_ORDER == LITTLE_ENDIAN
#define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \
|(rol(block->l[i],8)&0x00FF00FF))
#elif BYTE_ORDER == BIG_ENDIAN
#define blk0(i) block->l[i]
#else
#error "Endianness not defined!"
#endif
#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
^block->l[(i+2)&15]^block->l[i&15],1))
/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
/* Hash a single 512-bit block. This is the core of the algorithm. */
void SHA1Transform(u_int32_t state[5], const unsigned char buffer[64])
{
u_int32_t a, b, c, d, e;
typedef union {
unsigned char c[64];
u_int32_t l[16];
} CHAR64LONG16;
#ifdef SHA1HANDSOFF
CHAR64LONG16 block[1]; /* use array to appear as a pointer */
memcpy(block, buffer, 64);
#else
/* The following had better never be used because it causes the
* pointer-to-const buffer to be cast into a pointer to non-const.
* And the result is written through. I threw a "const" in, hoping
* this will cause a diagnostic.
*/
CHAR64LONG16* block = (const CHAR64LONG16*)buffer;
#endif
/* Copy context->state[] to working vars */
a = state[0];
b = state[1];
c = state[2];
d = state[3];
e = state[4];
/* 4 rounds of 20 operations each. Loop unrolled. */
R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
/* Add the working vars back into context.state[] */
state[0] += a;
state[1] += b;
state[2] += c;
state[3] += d;
state[4] += e;
/* Wipe variables */
a = b = c = d = e = 0;
#ifdef SHA1HANDSOFF
memset(block, '\0', sizeof(block));
#endif
}
/* SHA1Init - Initialize new context */
void SHA1Init(SHA1_CTX* context)
{
/* SHA1 initialization constants */
context->state[0] = 0x67452301;
context->state[1] = 0xEFCDAB89;
context->state[2] = 0x98BADCFE;
context->state[3] = 0x10325476;
context->state[4] = 0xC3D2E1F0;
context->count[0] = context->count[1] = 0;
}
/* Run your data through this. */
void SHA1Update(SHA1_CTX* context, const unsigned char* data, u_int32_t len)
{
u_int32_t i;
u_int32_t j;
j = context->count[0];
if ((context->count[0] += len << 3) < j)
context->count[1]++;
context->count[1] += (len>>29);
j = (j >> 3) & 63;
if ((j + len) > 63) {
memcpy(&context->buffer[j], data, (i = 64-j));
SHA1Transform(context->state, context->buffer);
for ( ; i + 63 < len; i += 64) {
SHA1Transform(context->state, &data[i]);
}
j = 0;
}
else i = 0;
memcpy(&context->buffer[j], &data[i], len - i);
}
/* Add padding and return the message digest. */
void SHA1Final(unsigned char digest[20], SHA1_CTX* context)
{
unsigned i;
unsigned char finalcount[8];
unsigned char c;
#if 0 /* untested "improvement" by DHR */
/* Convert context->count to a sequence of bytes
* in finalcount. Second element first, but
* big-endian order within element.
* But we do it all backwards.
*/
unsigned char *fcp = &finalcount[8];
for (i = 0; i < 2; i++)
{
u_int32_t t = context->count[i];
int j;
for (j = 0; j < 4; t >>= 8, j++)
*--fcp = (unsigned char) t
}
#else
for (i = 0; i < 8; i++) {
finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
>> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */
}
#endif
c = 0200;
SHA1Update(context, &c, 1);
while ((context->count[0] & 504) != 448) {
c = 0000;
SHA1Update(context, &c, 1);
}
SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */
for (i = 0; i < 20; i++) {
digest[i] = (unsigned char)
((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
}
/* Wipe variables */
memset(context, '\0', sizeof(*context));
memset(&finalcount, '\0', sizeof(finalcount));
}
/* ================ end of sha1.c ================ */
String
gen_sha1(String s, bool as_binary)
{
unsigned char v[20];
SHA1_CTX ctx;
SHA1Init(&ctx);
SHA1Update(&ctx, (const unsigned char *)s.data(), s.length());
SHA1Final(v, &ctx);
String result;
if(as_binary)
for(int i=0; i<20; i++)
result.append(1, v[i]);
else
for(int i=0; i<20; i++)
result += to_hex(v[i], 2);
return(result);
}
#define BIT_NOISE1 0xB5297A4D
#define BIT_NOISE2 0x68E31DA4
#define BIT_NOISE3 0x1B56C4E9
// based on Squirrel3 https://www.youtube.com/watch?v=LWFzPP8ZbdU&t=2666s
u32 gen_noise32(u32 index, u32 seed)
{
u32 r = index;
r *= BIT_NOISE1;
r += seed;
r ^= (r >> 8);
r += BIT_NOISE2;
r ^= (r << 8);
r *= BIT_NOISE3;
r ^= (r >> 8);
return(r);
}
#define BIT_NOISE61 0x5134811636f8cc8a
#define BIT_NOISE62 0xb8E31DA41B56C4E9
#define BIT_NOISE63 0x18cd227aaa1168c1
u64 gen_noise64(u64 index, u64 seed)
{
u64 r = index;
r *= BIT_NOISE61;
r += seed;
r ^= (r >> 8);
r += BIT_NOISE62;
r ^= (r << 8);
r *= BIT_NOISE63;
r ^= (r >> 8);
return(r);
}
#define MAX_64 0xffffffffffffffff
f64 gen_noise01(u64 index, u64 seed)
{
return((float)gen_noise64(index, seed)/(float)MAX_64);
}
u64 gen_int(u64 from, u64 to, u64 index, u64 seed)
{
u64 b = 1 + to - from;
return(from + (gen_noise64(index, seed) % b));
}
#include <tgmath.h>
f64 gen_float(f64 from, f64 to, u64 index, u64 seed, f64 decimal_precision)
{
f64 b = to - from;
return(from + fmod( decimal_precision*(f64)gen_noise64(index, seed), b));
}
u64 draw_int(u64 from, u64 to)
{
return(gen_int(from, to, context->random_index++, context->random_seed));
}
f64 draw_float(f64 from, f64 to, f64 decimal_precision)
{
return(gen_float(from, to, context->random_index++, context->random_seed, decimal_precision));
}
+19
View File
@@ -0,0 +1,19 @@
/* ================ sha1.h ================ */
/*
SHA-1 in C
By Steve Reid <steve@edmweb.com>
100% Public Domain
*/
String gen_sha1(String s, bool as_binary = false);
u32 gen_noise32(u32 index, u32 seed = 0);
u64 gen_noise64(u64 index, u64 seed = 0);
f64 gen_noise01(u64 index, u64 seed = 0);
u64 gen_int(u64 from, u64 to, u64 index, u64 seed = 0);
f64 gen_float(f64 from, f64 to, u64 index, u64 seed = 0, f64 decimal_precision = 0.000000000001);
u64 draw_int(u64 from, u64 to);
f64 draw_float(f64 from, f64 to, f64 decimal_precision = 0.000000000001);
+304
View File
@@ -0,0 +1,304 @@
#include "../3rdparty/mysql/mysql.h"
#include <stdio.h>
#include <stdlib.h>
#include "mysql-connector.h"
bool MySQL::connect(String host, String username, String password)
{
switch_to_system_alloc();
connection = mysql_init(NULL);
if (connection == NULL)
{
auto e = mysql_error((MYSQL*)connection);
fprintf(stderr, "%s\n", e);
switch_to_arena(context->mem);
statement_info.assign(e);
return(false);
}
if (mysql_real_connect((MYSQL*)connection, host.c_str(), username.c_str(), password.c_str(),
NULL, 0, NULL, 0) == NULL)
{
auto e = mysql_error((MYSQL*)connection);
fprintf(stderr, "%s\n", e);
mysql_close((MYSQL*)connection);
switch_to_arena(context->mem);
statement_info.assign(e);
return(false);
}
/*
if (mysql_query(con, "CREATE DATABASE testdb"))
{
fprintf(stderr, "%s\n", mysql_error(con));
mysql_close(con);
exit(1);
}
*/
switch_to_arena(context->mem);
statement_info = String("connected");
context->resources.mysql_connections.push_back(connection);
return(true);
}
String MySQL::escape(String raw, char quote_char)
{
return(mysql_escape(raw, quote_char));
}
String mysql_escape(String raw, char quote_char)
{
String result;
if(quote_char > 0)
result.append(1, quote_char);
for(u32 i = 0; i < raw.length(); i++)
{
char c = raw[i];
switch(c)
{
case('\n'):
result.append("\\n");
break;
case('\r'):
result.append("\\r");
break;
case('\t'):
result.append("\\t");
break;
case('\\'):
case('\''):
case('"'):
result.append(1, '\\');
result.append(1, c);
break;
default:
result.append(1, c);
break;
}
}
if(quote_char > 0)
result.append(1, quote_char);
return(result);
}
DTree field_to_dtree_node(char* data_ptr, MySQLFieldInfo field_info, u32 len)
{
DTree result;
switch(field_info.type)
{
case(MYSQL_TYPE_TINY):
result.set(atoll(data_ptr));
break;
case(MYSQL_TYPE_SHORT):
result.set(atoll(data_ptr));
break;
case(MYSQL_TYPE_LONG):
result.set(atoll(data_ptr));
break;
case(MYSQL_TYPE_INT24):
result.set(atoll(data_ptr));
break;
case(MYSQL_TYPE_LONGLONG):
result.set(atoll(data_ptr));
break;
case(MYSQL_TYPE_FLOAT):
result.set(atof(data_ptr));
break;
case(MYSQL_TYPE_DOUBLE):
result.set(atof(data_ptr));
break;
case(MYSQL_TYPE_NULL):
break;
default:
String s;
s.assign(data_ptr);
result.set(s);
break;
}
return(result);
}
DTree MySQL::get_pending_result()
{
DTree result_data;
// based on: https://dev.mysql.com/doc/c-api/5.7/en/mysql-field-count.html
MYSQL_RES *result;
result = mysql_store_result((MYSQL*)connection);
insert_id = mysql_insert_id((MYSQL*)connection);
//statement_info.assign(mysql_info((MYSQL*)connection));
if (result) // there are rows
{
field_count = mysql_num_fields(result);
row_count = mysql_num_rows(result);
field_info.clear();
unsigned int i;
MYSQL_FIELD *fields;
fields = mysql_fetch_fields(result);
for(i = 0; i < field_count; i++)
{
MySQLFieldInfo fi;
if(fields[i].name) fi.name.assign(fields[i].name);
if(fields[i].table) fi.table.assign(fields[i].table);
if(fields[i].db) fi.db.assign(fields[i].db);
fi.length = (fields[i].length);
if(fields[i].def) fi.def.assign(fields[i].def);
fi.max_length = (fields[i].max_length);
fi.flags = (fields[i].flags);
fi.type = (fields[i].type);
field_info.push_back(fi);
}
MYSQL_ROW row;
while ((row = mysql_fetch_row(result)))
{
DTree row_data;
auto lengths = mysql_fetch_lengths(result);
for(i = 0; i < field_count; i++)
{
row_data[field_info[i].name] = field_to_dtree_node(row[i], field_info[i], lengths[i]);
}
result_data.push(row_data);
}
mysql_free_result(result);
}
else // mysql_store_result() returned nothing; should it have?
{
if(mysql_field_count((MYSQL*)connection) == 0)
{
// query does not return data
// (it was not a SELECT)
affected_rows = mysql_affected_rows((MYSQL*)connection);
}
else // mysql_store_result() should have returned data
{
// error
}
}
return(result_data);
}
DTree MySQL::query(String q)
{
_preload_next_error_code = mysql_query((MYSQL*)connection, q.c_str());
DTree result;
if(_preload_next_error_code == 0)
result = get_pending_result();
return(result);
}
DTree MySQL::query(String q, StringMap params)
{
return(query(
parse_query_parameters(q, params).c_str()
));
}
String MySQL::parse_query_parameters(String query, StringMap map)
{
String result;
query.append(1, ' ');
u8 mode = 0;
char quote;
String identifier;
for(u32 i = 0; i < query.length(); i++)
{
char c = query[i];
if(mode == 0) // normal, unquoted mode
{
if(c == ':')
{
mode = 1;
identifier = "";
}
else if(c == '"' || c == '\'')
{
result.append(1, c);
mode = 2;
quote = c;
}
else
{
result.append(1, c);
}
}
else if(mode == 1) // identifier mode
{
if(isalnum(c))
{
identifier.append(1, c);
}
else
{
result.append(escape(map[identifier]));
result.append(1, c);
mode = 0;
}
}
else if(mode == 2) // quoted mode
{
if(c == quote)
mode = 0;
result.append(1, c);
}
}
return(result);
}
void MySQL::disconnect()
{
if(connection)
mysql_close((MYSQL*)connection);
connection = NULL;
}
String MySQL::error()
{
if(_preload_next_error_code)
{
String p = "Unknown error";
switch(_preload_next_error_code)
{
case(CR_COMMANDS_OUT_OF_SYNC):
p = "Commands out of sync";
break;
case(CR_SERVER_GONE_ERROR):
p = "Server connection error";
break;
case(CR_SERVER_LOST):
p = "Server hung up";
break;
case(CR_OUT_OF_MEMORY):
p = "Out of memory";
break;
default:
case(CR_UNKNOWN_ERROR):
p = "Unknown server error";
break;
}
_preload_next_error_code = 0;
return(p);
}
const char* res = mysql_error((MYSQL*)connection);
if(res)
{
return(String(res));
}
else
{
return("");
}
}
void cleanup_mysql_connections()
{
switch_to_system_alloc();
for(auto& con : context->resources.mysql_connections)
mysql_close((MYSQL*)con);
switch_to_arena(context->mem);
}
+67
View File
@@ -0,0 +1,67 @@
struct MySQLFieldInfo {
String name;
String table;
String db;
u64 length;
String def;
u64 max_length;
u64 flags;
u32 type;
};
struct MySQL {
void* connection = 0;
u32 _preload_next_error_code = 0;
u32 affected_rows = 0;
u32 field_count = 0;
u32 row_count = 0;
u64 insert_id = 0;
String statement_info = ""; //
std::vector<MySQLFieldInfo> field_info;
bool connect(String host = "localhost", String username = "root", String password = "");
void disconnect();
String error();
String escape(String raw, char quote_char = '\'');
String parse_query_parameters(String query, StringMap m);
DTree query(String q);
DTree query(String q, StringMap params);
DTree get_pending_result();
};
MySQL* mysql_connect(String host = "localhost", String username = "root", String password = "")
{
MySQL* m = new MySQL();
m->connect(host, username, password);
return(m);
}
void mysql_disconnect(MySQL* m)
{
m->disconnect();
}
String mysql_error(MySQL* m)
{
return(m->error());
}
String mysql_escape(String raw, char quote_char);
DTree mysql_query(MySQL* m, String q)
{
return(m->query(q));
}
DTree mysql_query(MySQL* m, String q, StringMap params)
{
return(m->query(q, params));
}
u64 mysql_insert_id(MySQL* m)
{
return(m->insert_id);
}
+497
View File
@@ -0,0 +1,497 @@
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <execinfo.h>
#include <fcntl.h>
#include <sys/file.h>
#include "sys.h"
String shell_exec(String cmd)
{
printf("(i) shell_exec(%s)\n", cmd.c_str());
String data;
FILE * stream;
const int max_buffer = 256;
char buffer[max_buffer];
cmd.append(" 2>&1");
stream = popen(cmd.c_str(), "r");
if (stream) {
while (!feof(stream))
if (fgets(buffer, max_buffer, stream) != NULL) data.append(buffer);
pclose(stream);
}
return data;
}
String shell_escape(String raw)
{
// FIXME
return("\"" + raw + "\"");
/*
` U+0060 (Grave Accent) Backtick Command substitution
~ U+007E Tilde Tilde expansion
! U+0021 Exclamation mark History expansion
# U+0023 Number sign Hash Comments
$ U+0024 Dollar sign Parameter expansion
& U+0026 Ampersand Background commands
* U+002A Asterisk Filename expansion and globbing
( U+0028 Left Parenthesis Subshells
) U+0029 Right Parenthesis Subshells
U+0009 Tab(⇥) Word splitting (whitespace)
{ U+007B Left Curly Bracket Left brace Brace expansion
[ U+005B Left Square Bracket Filename expansion and globbing
| U+007C Vertical Line Vertical bar Pipelines
\ U+005C Reverse Solidus Backslash Escape character
; U+003B Semicolon Separating commands
' U+0027 Apostrophe Single quote String quoting
" U+0022 Quotation Mark Double quote String quoting with interpolation
↩ U+000A Line Feed Newline Line break
< U+003C Less than Input redirection
> U+003E Greater than Output redirection
? U+003F Question mark Filename expansion and globbing
U+0020 Space Word splitting1 (whitespace)
*/
}
String basename(String fn)
{
String result;
while(fn.length() > 0)
result = nibble("/", fn);
//printf("basename(%s) %s\n", fn.c_str(), result.c_str());
return(result);
}
String dirname(String fn)
{
String result;
auto seg = split(fn, "/");
seg.pop_back();
result = join(seg, "/");
//printf("dirname(%s) %s seg#%i\n", fn.c_str(), result.c_str(), seg.size());
return(result);
}
bool mkdir(String path)
{
shell_exec(String("mkdir -p ")+" "+shell_escape(path));
return(true);
}
bool file_exists(String path)
{
std::filesystem::path fp{ path };
return(std::filesystem::exists(fp));
}
String file_get_contents(String file_name)
{
/*std::ifstream ifs(file_name);
printf("stream file desc %i\n", ifs.filedesc());
String content(
(std::istreambuf_iterator<char>(ifs) ),
(std::istreambuf_iterator<char>() ) );*/
char buf[512];
String content;
s32 fd = open(file_name.c_str(), O_RDONLY);
if(fd == -1)
{
printf("(!) Could not read %s\n", file_name.c_str());
return("");
}
flock(fd, LOCK_SH);
s64 bytes_read = 0;
//s64 size = lseek(fd, 0, SEEK_END);
//lseek(fd, 0, SEEK_SET);
//content.reserve(size+1);
while((bytes_read = read(fd, buf, 512)) > 0)
{
content.append(buf, bytes_read);
}
flock(fd, LOCK_UN);
close(fd);
return(content);
}
bool file_put_contents(String file_name, String content)
{
s32 fd = open(file_name.c_str(), O_WRONLY | O_CREAT | O_TRUNC);
if(fd == -1)
{
printf("(!) Could not write %s\n", file_name.c_str());
return(false);
}
flock(fd, LOCK_EX);
write(fd, content.data(), content.length());
flock(fd, LOCK_UN);
close(fd);
return(true);
}
String get_cwd()
{
return(std::filesystem::current_path());
}
void set_cwd(String path)
{
chdir(path.c_str());
}
time_t file_mtime(String file_name)
{
struct stat info;
if (stat(file_name.c_str(), &info) != 0)
{
return(0);
}
else
{
return(info.st_mtime);
}
}
void unlink(String file_name)
{
remove(file_name.c_str());
}
String expand_path(String path)
{
String result;
auto base_path = split(get_cwd(), "/");
auto rel_path = split(path, "/");
for(auto& s : rel_path)
{
if(s == "..")
{
base_path.pop_back();
}
else if(s == ".")
{
}
else
{
base_path.push_back(s);
}
}
return(join(base_path, "/"));
}
f64 microtime()
{
return ((f64)std::chrono::duration_cast<std::chrono::microseconds>(
std::chrono::high_resolution_clock::now().time_since_epoch()).count()) / 1000000;
}
u64 time()
{
return(std::time(0));
}
String date(String format, u64 timestamp)
{
String ts;
String fmt;
if(timestamp > 0)
ts = String("-d '@")+std::to_string(timestamp)+"'";
if(format != "")
fmt = String("+'"+format+"'");
return(trim(shell_exec("date "+ts+" "+fmt)));
}
String gmdate(String format, u64 timestamp)
{
String ts;
String fmt;
if(timestamp > 0)
ts = String("-d '@")+std::to_string(timestamp)+"'";
if(format == "RFC1123")
format = "%a, %d %b %Y %T GMT";
if(format != "")
fmt = String("+'"+format+"'");
return(trim(shell_exec("date -u "+ts+" "+fmt)));
}
u64 parse_time(String time_String)
{
return(int_val(trim(shell_exec("date -u -d '"+time_String+"' +'%s'"))));
}
u64 socket_connect(String host, short port)
{
/*String addrinfo {
int ai_flags;
int ai_family;
int ai_socktype;
int ai_protocol;
socklen_t ai_addrlen;
String sockaddr *ai_addr;
char *ai_canonname;
String addrinfo *ai_next;
};*/
auto sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(sockfd < 0)
{
print("SOCKET ERROR (could not open socket)\n");
perror("SOCKET ERROR ");
return(0);
}
struct sockaddr_in addr = {0};
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = inet_addr(host.c_str());
if(connect(sockfd, (struct sockaddr*) &addr, sizeof(addr)) < 0)
{
print("SOCKET ERROR (could not connect to address " + String(host) + ":" + std::to_string(port) + ")\n");
perror("SOCKET ERROR ");
return(0);
}
context->resources.sockets.push_back(sockfd);
return(sockfd);
}
void socket_close(u64 sockfd)
{
close(sockfd);
}
bool socket_write(u64 sockfd, String data)
{
return(write(sockfd, data.c_str(), data.length()) >= 0);
}
String socket_read(u64 sockfd, u32 max_length, u32 timeout)
{
struct timeval tv;
tv.tv_sec = timeout;
tv.tv_usec = 0;
setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof tv);
char buf[max_length+1];
auto byte_count = recv(sockfd, buf, sizeof(buf), 0);
if(byte_count > 0)
{
buf[byte_count] = 0;
String result(buf, byte_count+1);
return(result);
}
return("");
}
String memcache_escape_key(String key)
{
String result;
for(auto c : key)
{
if(isspace(c))
c = '_';
result.append(1, c);
}
return(result);
}
StringList memcache_escape_keys(StringList keys)
{
StringList result;
for(auto s : keys)
{
result.push_back(memcache_escape_key(s));
}
return(result);
}
u64 memcache_connect(String host, short port)
{
return(socket_connect(host, port));
}
String memcache_command(u64 connection, String command)
{
socket_write(connection, command+"\r\n");
return(socket_read(connection)); // FIXME: do multi-chunk until END line is received!
}
bool memcache_set(u64 connection, String key, String value, u64 expires_in)
{
socket_write(connection,
// set KEY META_DATA EXPIRY_TIME LENGTH_IN_BYTES
String("set ") + memcache_escape_key(key) + " 0 " + std::to_string(expires_in) + " " + std::to_string(value.length()) + "\r\n" +
value + "\r\n");
return("STORED" == trim(socket_read(connection)));
}
bool memcache_delete(u64 connection, String key)
{
socket_write(connection,
// set KEY META_DATA EXPIRY_TIME LENGTH_IN_BYTES
String("delete ") + memcache_escape_key(key) + "\r\n"
);
return("DELETED" == trim(socket_read(connection)));
}
String memcache_get(u64 connection, String key, String default_value)
{
auto res = memcache_command(connection, String("get ")+memcache_escape_key(key));
String t = nibble(res, " ");
if(t == "VALUE")
{
String key = nibble(res, " ");
String meta = nibble(res, " ");
u32 length = stoi(nibble(res, "\r\n"));
return(res.substr(0, length));
}
return(default_value);
}
StringMap memcache_get_multiple(u64 connection, StringList keys)
{
StringMap result;
// to do: escape key String
auto res = memcache_command(connection, String("get ")+join(memcache_escape_keys(keys), " "));
while(res.length() > 0)
{
String t = nibble(res, " ");
if(t == "VALUE")
{
String key = nibble(res, " ");
String meta = nibble(res, " ");
u32 length = stoi(nibble(res, "\r\n"));
result[key] = res.substr(0, length);
res = res.substr(length+2);
}
}
return(result);
}
void on_segfault(int sig)
{
void *array[10];
size_t size;
// get void*'s for all entries on the stack
size = backtrace(array, 10);
// print out all the frames to stderr
fprintf(stderr, "SEG FAULT: %d:\n", sig);
backtrace_symbols_fd(array, size, STDERR_FILENO);
exit(1);
}
struct Worker {
pid_t pid;
};
std::map<pid_t, Worker> workers;
#include <sys/wait.h>
#include <sys/resource.h>
#include <sys/prctl.h>
void spawn_subprocess(std::function<void()> exec_after_spawn)
{
parent_pid = getpid();
pid_t p;
p = fork();
if(p == 0)
{
my_pid = getpid();
//printf("(C) child procress started, PID:%i\n", my_pid);
prctl(PR_SET_PDEATHSIG, SIGHUP);
exec_after_spawn();
}
else
{
Worker w;
w.pid = p;
workers[w.pid] = w;
printf("(P) child procress spawned: PID %i\n", p);
}
}
pid_t task_pid(String key)
{
String status_file_name = context->server->config.BIN_DIRECTORY + "/task-" + key;
String status_file = file_get_contents(status_file_name);
pid_t p = 0;
if(status_file != "")
{
p = int_val(status_file);
if(kill(p, 0) == 0) // process is still running
return(p);
unlink(status_file_name);
}
return(p);
}
pid_t task(String key, std::function<void()> exec_after_spawn, u64 timeout)
{
String status_file_name = context->server->config.BIN_DIRECTORY + "/task-" + key;
String status_file = file_get_contents(status_file_name);
pid_t p;
if(status_file != "")
{
p = int_val(status_file);
if(kill(p, 0) == 0) // process is still running
{
printf("(P) worker process '%s' already running: PID %i\n", key.c_str(), p);
return(p);
}
//printf("(P) worker process '%s' had crashed: PID %i\n", key.c_str(), p);
unlink(status_file_name);
}
p = fork();
if(p == 0)
{
my_pid = getpid();
file_put_contents(status_file_name, std::to_string(my_pid));
close(context->resources.fcgi_socket);
context->resources.fcgi_socket = 0;
//printf("(C) child procress started, PID:%i\n", my_pid);
//prctl(PR_SET_PDEATHSIG, SIGHUP);
exec_after_spawn();
unlink(status_file_name);
printf("(P) worker process '%s' terminated: PID %i\n", key.c_str(), my_pid);
exit(0);
}
else
{
printf("(P) worker process '%s' spawned: PID %i\n", key.c_str(), p);
return(p);
}
}
void on_child_exit(int sig)
{
pid_t pid;
int status;
if ((pid = waitpid(-1, &status, WNOHANG)) != -1)
{
if(workers.count(pid) > 0)
{
workers.erase(pid);
printf("(P) child terminated (PID:%i)\n", pid);
//spawn_subprocess();
}
}
}
StringList ls(String dir)
{
return(split(trim(shell_exec("ls -1 "+shell_escape(dir))), "\n"));
}
+54
View File
@@ -0,0 +1,54 @@
#include <signal.h>
String shell_exec(String cmd);
String shell_escape(String raw);
String basename(String fn);
String dirname(String fn);
bool mkdir(String path);
bool file_exists(String path);
String file_get_contents(String file_name);
bool file_put_contents(String file_name, String content);
#include <fstream>
template <typename... Ts>
bool file_append(String file_name, Ts... args)
{
std::ofstream fout;
fout.open(file_name.c_str(), std::ios_base::app);
((fout << args), ...);
fout.close();
return(true);
}
String get_cwd();
void set_cwd(String path);
time_t file_mtime(String file_name);
void unlink(String file_name);
String expand_path(String path);
StringList ls(String dir);
f64 microtime();
u64 time();
String date(String format = "", u64 timestamp = 0);
String gmdate(String format = "", u64 timestamp = 0);
u64 parse_time(String time_String);
u64 socket_connect(String host, short port);
void socket_close(u64 sockfd);
bool socket_write(u64 sockfd, String data);
String socket_read(u64 sockfd, u32 max_length = 1024*128, u32 timeout = 1);
String memcache_escape_key(String key);
StringList memcache_escape_keys(StringList keys);
u64 memcache_connect(String host = "127.0.0.1", short port = 11211);
String memcache_command(u64 connection, String command);
bool memcache_set(u64 connection, String key, String value, u64 expires_in = 60*60);
bool memcache_delete(u64 connection, String key);
String memcache_get(u64 connection, String key, String default_value = "");
StringMap memcache_get_multiple(u64 connection, StringList keys);
pid_t parent_pid = 0;
pid_t my_pid = 0;
void on_segfault(int sig);
pid_t task(String key, std::function<void()> exec_after_spawn, u64 timeout = 60*10);
pid_t task_pid(String key);
+63
View File
@@ -0,0 +1,63 @@
#include <stdlib.h>
#include <unistd.h>
#include <iostream>
#include <filesystem>
#include <ctype.h>
#include <fstream>
#include <sys/stat.h>
#include <ctime>
#include <dlfcn.h>
#include <limits.h>
#include <algorithm>
#include <sys/stat.h>
#include <iostream>
#include "types.h"
SharedUnit::~SharedUnit()
{
if(so_handle)
{
dlclose(so_handle);
}
}
String nibble(String div, String& haystack)
{
auto pos = haystack.find(div);
if(pos == String::npos)
{
auto result = haystack;
haystack.clear();
return(result);
}
else
{
auto result = haystack.substr(0, pos);
haystack.erase(0, pos+div.length());
return(result);
}
}
void Request::invoke(String file_name)
{
DTree call_param;
compiler_invoke(this, file_name, call_param);
}
void Request::invoke(String file_name, DTree& call_param)
{
compiler_invoke(this, file_name, call_param);
}
void Request::ob_start()
{
ob_stack.push_back(new std::ostringstream());
ob = ob_stack.back();
}
Request::~Request()
{
for(auto& sockfd : resources.sockets)
close(sockfd);
}
+294
View File
@@ -0,0 +1,294 @@
#include <string>
#include <map>
#include <list>
#include <vector>
#include <functional>
#include <sstream>
typedef unsigned char u8;
typedef signed char s8;
typedef unsigned short u16;
typedef signed short s16;
typedef unsigned int u32;
typedef signed int s32;
typedef float f32;
typedef double f64;
typedef unsigned long long u64;
typedef long long s64;
typedef std::string String;
String operator+(String lhs, u64 rhs) {
return(lhs + std::to_string(rhs));
}
String operator+(String lhs, u32 rhs) {
return(lhs + std::to_string(rhs));
}
String operator+(String lhs, s64 rhs) {
return(lhs + std::to_string(rhs));
}
String operator+(String lhs, s32 rhs) {
return(lhs + std::to_string(rhs));
}
String operator+(String lhs, f64 rhs) {
return(lhs + std::to_string(rhs));
}
String operator+(String lhs, f32 rhs) {
return(lhs + std::to_string(rhs));
}
#define DEBUG_MEMORY_OFF
#define GLOBAL_ARENA_ALLOCATOR
struct MemoryArena {
u8* data;
u64 size = 0;
u64 capacity = 0;
String name = "unnamed";
MemoryArena(u64 cap, String _name = "unnamed")
{
name = _name;
capacity = cap;
printf("(i) memory arena '%s' created with capacity of %llu bytes\n", name.c_str(), capacity);
data = (u8*)malloc(cap);
}
~MemoryArena()
{
free(data);
}
void clear()
{
#ifdef DEBUG_MEMORY
printf("(i) memory arena '%s' cleared after high mark of %llu bytes\n", name.c_str(), size);
#endif
size = 0;
}
void* get(u64 size_needed)
{
u64 size_aligned = 8 + (8 * ((size_needed) / 8));
u8* result = data + size;
if(size_aligned + size >= capacity)
{
printf("(!) memory arena '%s' capacity (%llu) exceeded %llu/%llu + %llu >= %llu\n",
name.c_str(), capacity, size_needed, size_aligned, size, capacity);
return(0);
}
size += size_aligned;
#ifdef DEBUG_MEMORY_DETAILED
printf("(i) memory arena '%s' [+%llu]:%p alloc %llu/%llu bytes\n", name.c_str(), size, result, size_needed, size_aligned);
#endif
return(result);
}
};
MemoryArena* current_memory_arena = 0;
void switch_to_system_alloc()
{
#ifdef GLOBAL_ARENA_ALLOCATOR
current_memory_arena = 0;
#endif
}
void switch_to_arena(MemoryArena* a)
{
#ifdef GLOBAL_ARENA_ALLOCATOR
current_memory_arena = a;
#endif
}
#ifdef GLOBAL_ARENA_ALLOCATOR
void * operator new(decltype(sizeof(0)) n) noexcept(false)
{
if(current_memory_arena)
{
return(current_memory_arena->get(n));
}
else
{
return(malloc(n));
}
}
void operator delete(void * p) throw()
{
if(current_memory_arena)
{
}
else
{
free(p);
}
}
#endif
typedef std::map<String, String> StringMap;
typedef std::vector<String> StringList;
struct Request;
struct DTree;
typedef void (*call_handler)(DTree& call_param);
typedef void (*request_handler)(Request* request);
String to_string(s64 v) { return(std::to_string(v)); }
struct ServerSettings {
String BIN_DIRECTORY = "/tmp/uce/work";
String COMPILE_SCRIPT = "scripts/compile";
String LIT_ESC = "3d5b5_1";
String CONTENT_TYPE = "text/html; charset=utf-8";
String SOCKET_PATH = "/run/uce.sock";
String TMP_UPLOAD_PATH = "/tmp/uce/uploads";
String SESSION_PATH = "/tmp/uce/sessions";
String COMPILER_SYS_PATH = ".";
u32 LISTEN_PORT = 9993;
u64 SESSION_TIME = 60*60*24*30;
u32 WORKER_COUNT = 4;
u32 MAX_MEMORY = 1024*1024*16;
};
struct SharedUnit {
String file_name;
String so_name;
String src_path;
String bin_path;
String pre_path;
String src_file_name;
String bin_file_name;
String pre_file_name;
void* so_handle;
request_handler on_setup;
call_handler on_render;
String compiler_messages;
time_t last_compiled;
~SharedUnit();
};
struct UploadedFile {
String file_name;
String tmp_name;
u32 size;
};
struct ServerState {
std::map<String, SharedUnit*> units;
ServerSettings config;
u32 request_count = 0;
};
struct URI {
StringMap query;
StringMap parts;
};
String nibble(String div, String& haystack);
template <typename ITYPE>
String to_hex(ITYPE w, size_t hex_len = sizeof(ITYPE)<<1)
{
static const char* digits = "0123456789ABCDEF";
String rc(hex_len,'0');
for (size_t i=0, j=(hex_len-1)*4 ; i<hex_len; ++i,j-=4)
rc[i] = digits[(w>>j) & 0x0f];
return(rc);
}
#include "dtree.h"
void compiler_invoke(Request* context, String file_name, DTree& call_param);
struct Request {
ServerState* server;
StringMap params;
StringMap get;
StringMap post;
StringMap cookies;
StringMap session;
DTree var;
String session_id = "";
String session_name = "";
std::vector<UploadedFile> uploaded_files;
StringMap header;
StringList set_cookies;
u64 random_seed;
u64 random_index;
MemoryArena* mem;
String in;
std::vector<std::ostringstream*> ob_stack;
std::ostringstream* ob;
String out;
String err;
bool is_finished = false;
struct Flags {
bool log_request = true;
} flags;
struct Stats {
u32 bytes_written;
f64 time_init;
f64 time_start;
f64 time_end;
} stats;
struct Resources {
std::vector<u64> sockets;
std::vector<void*> mysql_connections;
u64 fcgi_socket = 0;
} resources;
void invoke(String file_name);
void invoke(String file_name, DTree& call_param);
void ob_start();
~Request();
};
typedef Request FastCGIRequest;
Request* context;
#include <iostream>
template <typename... Ts>
void print(Ts... args)
{
((*context->ob << args), ...);
}
+10
View File
@@ -0,0 +1,10 @@
#include "types.cpp"
#include "hash.cpp"
#include "dtree.cpp"
#include "functionlib.cpp"
#include "sys.cpp"
#include "uri.cpp"
#include "compiler.cpp"
#include "mysql-connector.cpp"
+14
View File
@@ -0,0 +1,14 @@
#include "types.h"
#include "hash.h"
#include "functionlib.h"
#include "sys.h"
#include "uri.h"
#include "compiler.h"
#include "mysql-connector.h"
extern "C" void set_current_request(Request* _request)
{
context = _request;
signal(SIGSEGV, on_segfault);
}
+3
View File
@@ -0,0 +1,3 @@
#include "uce_lib.h"
+321
View File
@@ -0,0 +1,321 @@
#include "uri.h"
String var_dump(URI uri, String prefix, String postfix)
{
return(
prefix + "URI Parts: " + postfix +
var_dump(uri.parts, prefix+" ", postfix)+
prefix + " Query: " + postfix +
var_dump(uri.query, prefix+" ", postfix)
);
}
String uri_decode(String q)
{
String result;
for(u32 i = 0; i < q.length(); i++)
{
char c = q[i];
if(c == '%' && q[i+1] != '%')
{
result.append(1, hex_to_u8(q.substr(i+1, 2)));
i += 2;
}
else if(c == '+')
{
result.append(1, ' ');
}
else
{
result.append(1, c);
}
}
return(result);
}
String uri_encode(String q)
{
String result;
for(u32 i = 0; i < q.length(); i++)
{
char c = q[i];
if(isalnum(c) || c == '~' || c == '.' || c == '_' || c == '-')
result.append(1, c);
else
{
result.append(1, '%');
result.append(to_hex(c));
}
}
return(result);
}
StringMap parse_query(String q)
{
StringMap result;
if(q.length() == 0)
return(result);
bool is_key = true;
String key = "";
String value = "";
for (char &c: q)
{
if(c == '=')
{
is_key = !is_key;
}
else if(c == '&')
{
result[uri_decode(key)] = uri_decode(value);
key = "";
value = "";
is_key = true;
}
else if(is_key)
{
key.append(1, c);
}
else
{
value.append(1, c);
}
}
result[uri_decode(key)] = uri_decode(value);
return(result);
}
String encode_query(StringMap map)
{
String result;
for (auto it = map.begin(); it != map.end(); ++it)
{
if(result.length() > 0)
result.append(1, '&');
result.append(uri_encode(it->first) + "=" + uri_encode(it->second));
}
return(result);
}
StringMap parse_multipart(String q, String boundary, std::vector<UploadedFile>& uploaded_files)
{
StringMap result;
auto i = boundary.length();
while(i < q.length())
{
auto end_pos = q.find(boundary, i);
if(end_pos != String::npos)
{
String field = q.substr(i, end_pos - i);
nibble(":", field);
String ftype = trim(nibble(";", field));
if(ftype == "form-data")
{
nibble("=\"", field);
String field_name = nibble("\"", field);
result[field_name] = field.substr(4, field.length()-6);
}
else if(ftype == "attachment")
{
nibble("=\"", field);
UploadedFile f;
f.tmp_name = context->server->config.TMP_UPLOAD_PATH + std::to_string(rand());
f.file_name = nibble("\"", field);
String bin = field.substr(4, field.length()-6);
f.size = bin.length();
file_put_contents(f.tmp_name, bin);
uploaded_files.push_back(f);
}
}
else
{
// we're done
end_pos = q.length();
}
i = end_pos + boundary.length();
}
return(result);
}
URI parse_uri(String uri_String)
{
URI result;
u8 state = 0;
String current = "";
char expect = 0;
result.parts["raw"] = uri_String;
String part_names[] = {
"scheme",
"host",
"port",
"path",
"query",
"fragment",
};
if(uri_String[0] == '/')
state = 3;
for (char &c: uri_String)
{
bool append_it = true;
if(expect && expect != c)
{
result.parts["error"] = String("\'") + c + String("\' expected");
result.parts["error_parsing"] = current;
result.parts["error_part"] = part_names[state];
return(result);
}
expect = 0;
switch(state)
{
case(0): // scheme
if(c == ':')
{
result.parts[part_names[state]] = current;
append_it = false;
current = "";
state = 1;
}
break;
case(1): // host name
if(c == '/')
{
if(current == "")
{
append_it = false;
break;
}
result.parts[part_names[state]] = current;
append_it = false;
current = "";
state = 3;
expect = '/';
}
else if(c == ':')
{
result.parts[part_names[state]] = current;
append_it = false;
current = "";
state = 2;
}
break;
case(2): // port
if(c == '/')
{
result.parts[part_names[state]] = current;
append_it = false;
current = "";
state = 3;
expect = '/';
}
break;
case(3): // path
if(c == '/' && current == "")
{
append_it = false;
break;
}
if(c == '?')
{
result.parts[part_names[state]] = current;
append_it = false;
current = "";
state = 4;
}
break;
case(4): // query
if(c == '#')
{
result.parts[part_names[state]] = current;
append_it = false;
current = "";
state = 5;
}
break;
}
if(append_it)
current.append(1, c);
}
result.parts[part_names[state]] = current;
result.query = parse_query(result.parts["query"]);
return(result);
}
void set_cookie(
String name, String value,
u64 expires, String path, String domain,
bool secure, bool http_only)
{
String cookie = "Set-Cookie: ";
cookie.append(uri_encode(name) + "=" + uri_encode(value));
if(expires > 0)
cookie.append(String("; Expires=") + gmdate("RFC1123", expires));
context->set_cookies.push_back(cookie);
context->cookies[name] = value;
}
StringMap parse_cookies(String cookie_String)
{
StringMap result;
while(cookie_String.length() > 0)
{
String key = trim(nibble("=", cookie_String));
String value = nibble(";", cookie_String);
result[key] = value;
}
return(result);
}
String make_session_id()
{
return(to_hex(rand())+to_hex(rand())+to_hex(rand())+to_hex(rand()));
}
StringMap load_session_data(String session_id)
{
return(parse_query(file_get_contents(context->server->config.SESSION_PATH + "/" + session_id)));
}
void save_session_data(String session_id, StringMap data)
{
file_put_contents(context->server->config.SESSION_PATH + "/" + session_id, encode_query(data));
}
String session_start(String session_name)
{
if(context->cookies[session_name].length() == 0)
{
set_cookie(session_name, make_session_id(), time() + context->server->config.SESSION_TIME);
}
context->session_id = context->cookies[session_name];
context->session_name = session_name;
context->session = load_session_data(context->session_id);
return(context->session_id);
}
void session_destroy(String session_name)
{
if(context->cookies[session_name].length() > 0)
{
set_cookie(session_name, "", time() - context->server->config.SESSION_TIME);
context->session.clear();
save_session_data(context->session_id, context->session);
context->session_id = "";
}
}
+19
View File
@@ -0,0 +1,19 @@
String var_dump(URI uri, String prefix = "", String postfix = "\n");
String uri_decode(String q);
String uri_encode(String q);
StringMap parse_query(String q);
String encode_query(StringMap map);
StringMap parse_multipart(String q, String boundary, std::vector<UploadedFile>& uploaded_files);
URI parse_uri(String uri_String);
void set_cookie(
String name, String value = "",
u64 expires = 0, String path = "/", String domain = "",
bool secure = false, bool http_only = true);
StringMap parse_cookies(String cookie_String);
String make_session_id();
StringMap load_session_data(String session_id);
void save_session_data(String session_id, StringMap data);
String session_start(String session_name = "uce-session");
void session_destroy(String session_name = "uce-session");
+157
View File
@@ -0,0 +1,157 @@
#include "lib/uce_lib.cpp"
ServerState server_state;
MemoryArena* request_arena = new MemoryArena(server_state.config.MAX_MEMORY, "request");
#include "fastcgi/src/fcgicc.cc"
FastCGIServer server;
int handle_request(FastCGIRequest& request) {
// This is always the first event to occur. It occurs when the
// server receives all parameters. There may be more data coming on the
// standard input stream.
if (request.params.count("REQUEST_URI"))
return 0; // OK, continue processing
else
return 1; // stop processing and return error code
}
int handle_data(FastCGIRequest& request) {
// This event occurs when data is received on the standard input stream.
// A simple String is used to hold the input stream, so it is the
// responsibility of the application to remember which data it has
// processed. The application may modify it; new data will be appended
// to it by the server. The same goes for the output and error streams:
// the application should append data to them; the server will remove
// all sent data from them.
return 0; // still OK
std::transform(request.in.begin(), request.in.end(),
std::back_inserter(request.err),
std::bind1st(std::plus<char>(), 1));
request.in.clear(); // don't process it again
return 0; // still OK
}
int handle_complete(FastCGIRequest& request) {
// The event handler can also be a class member function. This
// event occurs when the parameters and standard input streams are
// both closed, and thus the request is complete.
// printf("(i) request handle\n");
context = &request;
server_state.request_count += 1;
request.server = &server_state;
request.stats.time_start = microtime();
request.header["Content-Type"] = context->server->config.CONTENT_TYPE;
request.get = parse_query(request.params["QUERY_STRING"]);
request.random_index = 0;
request.random_seed = gen_noise64(*reinterpret_cast<u64*>(&request.stats.time_start));
request.ob_start();
if(request.params["HTTP_COOKIE"].length() > 0)
request.cookies = parse_cookies(request.params["HTTP_COOKIE"]);
String ct_info = request.params["CONTENT_TYPE"];
String ct_type = nibble(";", ct_info);
if(request.params["REQUEST_METHOD"] == "POST")
{
if(ct_type == "multipart/form-data")
{
nibble("boundary=", ct_info);
request.post = parse_multipart(request.in, String("--")+ct_info, request.uploaded_files);
}
else
{
request.post = parse_query(request.in);
}
}
// printf("(i) request ready\n");
request.invoke(request.params["SCRIPT_FILENAME"]);
for( auto &f : request.uploaded_files)
{
unlink(f.tmp_name);
}
if(request.session_id.length() > 0)
save_session_data(request.session_id, request.session);
cleanup_mysql_connections();
return 0;
}
void listen_for_connections()
{
signal(SIGSEGV, on_segfault);
server.on_request = &handle_request;
server.on_data = &handle_data;
server.on_complete = &handle_complete;
/*
server.request_handler(&handle_request);
server.data_handler(&handle_data);
server.complete_handler(&handle_complete);
*/
for(;;)
{
//if(request_arena) request_arena->clear();
//current_memory_arena = request_arena;
server.process();
}
}
int main(int argc, char** argv)
{
printf("(P) Starting parent server PID:%i\n", getpid());
signal(SIGCHLD, on_child_exit);
srand(time());
//if(server_state.config.COMPILER_SYS_PATH == "")
server_state.config.COMPILER_SYS_PATH = get_cwd();
// printf("MySQL client version: %s\n", mysql_get_client_info());
printf("Compiler base path: %s\n", server_state.config.COMPILER_SYS_PATH.c_str());
server_state.config.COMPILE_SCRIPT =
server_state.config.COMPILER_SYS_PATH + "/" + server_state.config.COMPILE_SCRIPT;
if(server_state.config.LISTEN_PORT)
server.listen(server_state.config.LISTEN_PORT);
if(server_state.config.SOCKET_PATH != "")
server.listen(server_state.config.SOCKET_PATH);
chmod(server_state.config.SOCKET_PATH.c_str(), S_IRWXU | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
dirname(server_state.config.COMPILER_SYS_PATH);
basename(server_state.config.COMPILER_SYS_PATH);
mkdir(server_state.config.BIN_DIRECTORY);
mkdir(server_state.config.TMP_UPLOAD_PATH);
mkdir(server_state.config.SESSION_PATH);
//server.process(100);
//server.process();
/*try
{
server.process_forever();
}
catch (const std::runtime_error& e)
{
std::cout << e.what();
}*/
for(;;)
{
while(workers.size() < server_state.config.WORKER_COUNT)
{
spawn_subprocess(listen_for_connections);
}
sleep(1);
}
return 0;
}