summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergei Golubchik <sergii@pisem.net>2011-10-01 21:23:01 +0200
committerSergei Golubchik <sergii@pisem.net>2011-10-01 21:23:01 +0200
commit4c34bcf8562fac61a918c6364e3bbbe385e8505d (patch)
treedd58f9392e4e8f4c9e0e8deba29894388c1fd6ff
downloadmariadb-git-4c34bcf8562fac61a918c6364e3bbbe385e8505d.tar.gz
initial checkin
-rw-r--r--CMakeLists.txt11
-rw-r--r--Makefile.am19
-rw-r--r--feedback.cc349
-rw-r--r--feedback.h67
-rw-r--r--plug.in19
-rw-r--r--sender_thread.cc294
-rw-r--r--url_base.cc51
-rw-r--r--url_http.cc308
-rw-r--r--utils.cc299
9 files changed, 1417 insertions, 0 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 00000000000..a94232a4698
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,11 @@
+INCLUDE("${PROJECT_SOURCE_DIR}/storage/mysql_storage_engine.cmake")
+
+INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/sql ${CMAKE_SOURCE_DIR}/regex
+ ${CMAKE_SOURCE_DIR}/extra/yassl/include)
+
+SET(FEEDBACK_SOURCES feedback.cc sender_thread.cc
+ url_base.cc url_http.cc utils.cc)
+
+SET(FEEDBACK_LIBS Ws2_32)
+
+MYSQL_PLUGIN(FEEDBACK)
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 00000000000..0aea977b464
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,19 @@
+pkgplugindir = $(pkglibdir)/plugin
+INCLUDES = -I$(top_srcdir)/include -I$(top_builddir)/include \
+ -I$(top_srcdir)/regex -I$(top_srcdir)/sql
+
+EXTRA_LTLIBRARIES = feedback.la
+pkgplugin_LTLIBRARIES = @plugin_feedback_shared_target@
+feedback_la_LDFLAGS = -module -rpath $(pkgplugindir)
+feedback_la_CXXFLAGS = -shared -DMYSQL_DYNAMIC_PLUGIN
+feedback_la_SOURCES = feedback.cc utils.cc url_base.cc url_http.cc \
+ sender_thread.cc
+
+EXTRA_LIBRARIES = libfeedback.a
+noinst_LIBRARIES = @plugin_feedback_static_target@
+libfeedback_a_SOURCES= feedback.cc utils.cc url_base.cc url_http.cc \
+ sender_thread.cc
+
+noinst_HEADERS = feedback.h
+EXTRA_DIST = CMakeLists.txt plug.in
+
diff --git a/feedback.cc b/feedback.cc
new file mode 100644
index 00000000000..89fe981cc25
--- /dev/null
+++ b/feedback.cc
@@ -0,0 +1,349 @@
+/* Copyright (C) 2010 Sergei Golubchik and Monty Program Ab
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ 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 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include "feedback.h"
+
+/* MySQL functions/variables not declared in mysql_priv.h */
+int fill_variables(THD *thd, TABLE_LIST *tables, COND *cond);
+int fill_status(THD *thd, TABLE_LIST *tables, COND *cond);
+extern ST_SCHEMA_TABLE schema_tables[];
+
+namespace feedback {
+
+char server_uid_buf[SERVER_UID_SIZE+1]; ///< server uid will be written here
+
+/* backing store for system variables */
+static char *server_uid= server_uid_buf, *url;
+char *user_info;
+ulong send_timeout, send_retry_wait;
+
+/**
+ these three are used to communicate the shutdown signal to the
+ background thread
+*/
+pthread_mutex_t sleep_mutex;
+pthread_cond_t sleep_condition;
+volatile bool shutdown_plugin;
+static pthread_t sender_thread;
+
+Url **urls; ///< list of urls to send the report to
+uint url_count;
+
+ST_SCHEMA_TABLE *i_s_feedback; ///< table descriptor for our I_S table
+
+/*
+ the column names *must* match column names in GLOBAL_VARIABLES and
+ GLOBAL_STATUS tables otherwise condition pushdown below will not work
+*/
+static ST_FIELD_INFO feedback_fields[] =
+{
+ {"VARIABLE_NAME", 255, MYSQL_TYPE_STRING, 0, 0, 0, 0},
+ {"VARIABLE_VALUE", 1024, MYSQL_TYPE_STRING, 0, 0, 0, 0},
+ {0, 0, MYSQL_TYPE_NULL, 0, 0, 0, 0}
+};
+
+static COND * const OOM= (COND*)1;
+
+/**
+ Generate the COND tree for the condition pushdown
+
+ This function takes a list of strings and generates an Item tree
+ corresponding to the following expression:
+
+ field LIKE str1 OR field LIKE str2 OR field LIKE str3 OR ...
+
+ where 'field' is the first field in the table - VARIABLE_NAME field -
+ and str1, str2... are strings from the list.
+
+ This condition is used to filter the selected rows, emulating
+
+ SELECT * FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES WHERE ...
+*/
+static COND* make_cond(THD *thd, TABLE_LIST *tables, LEX_STRING *filter)
+{
+ Item_cond_or *res= NULL;
+ Name_resolution_context nrc;
+ const char *db= tables->db, *table= tables->alias,
+ *field= tables->table->field[0]->field_name;
+ CHARSET_INFO *cs= &my_charset_latin1;
+
+ if (!filter->str)
+ return 0;
+
+ nrc.init();
+ nrc.resolve_in_table_list_only(tables);
+
+ res= new Item_cond_or();
+ if (!res)
+ return OOM;
+
+ for (; filter->str; filter++)
+ {
+ Item_field *fld= new Item_field(&nrc, db, table, field);
+ Item_string *pattern= new Item_string(filter->str, filter->length, cs);
+ Item_string *escape= new Item_string("\\", 1, cs);
+
+ if (!fld || !pattern || !escape)
+ return OOM;
+
+ Item_func_like *like= new Item_func_like(fld, pattern, escape, 0);
+
+ if (!like)
+ return OOM;
+
+ res->add(like);
+ }
+
+ if (res->fix_fields(thd, (Item**)&res))
+ return OOM;
+
+ return res;
+}
+
+/**
+ System variables that we want to see in the feedback report
+*/
+static LEX_STRING vars_filter[]= {
+ {C_STRING_WITH_LEN("auto\\_increment%")},
+ {C_STRING_WITH_LEN("binlog\\_format")},
+ {C_STRING_WITH_LEN("character\\_set\\_%")},
+ {C_STRING_WITH_LEN("collation%")},
+ {C_STRING_WITH_LEN("engine\\_condition\\_pushdown")},
+ {C_STRING_WITH_LEN("event\\_scheduler")},
+ {C_STRING_WITH_LEN("feedback\\_%")},
+ {C_STRING_WITH_LEN("ft\\_m%")},
+ {C_STRING_WITH_LEN("have\\_%")},
+ {C_STRING_WITH_LEN("%\\_size")},
+ {C_STRING_WITH_LEN("%\\_length%")},
+ {C_STRING_WITH_LEN("%\\_timeout")},
+ {C_STRING_WITH_LEN("large\\_%")},
+ {C_STRING_WITH_LEN("lc_time_names")},
+ {C_STRING_WITH_LEN("log")},
+ {C_STRING_WITH_LEN("log_bin")},
+ {C_STRING_WITH_LEN("log_output")},
+ {C_STRING_WITH_LEN("log_slow_queries")},
+ {C_STRING_WITH_LEN("log_slow_time")},
+ {C_STRING_WITH_LEN("lower_case%")},
+ {C_STRING_WITH_LEN("max_allowed_packet")},
+ {C_STRING_WITH_LEN("max_connections")},
+ {C_STRING_WITH_LEN("max_prepared_stmt_count")},
+ {C_STRING_WITH_LEN("max_sp_recursion_depth")},
+ {C_STRING_WITH_LEN("max_user_connections")},
+ {C_STRING_WITH_LEN("max_write_lock_count")},
+ {C_STRING_WITH_LEN("myisam_recover_options")},
+ {C_STRING_WITH_LEN("myisam_repair_threads")},
+ {C_STRING_WITH_LEN("myisam_stats_method")},
+ {C_STRING_WITH_LEN("myisam_use_mmap")},
+ {C_STRING_WITH_LEN("net\\_%")},
+ {C_STRING_WITH_LEN("new")},
+ {C_STRING_WITH_LEN("old%")},
+ {C_STRING_WITH_LEN("optimizer%")},
+ {C_STRING_WITH_LEN("profiling")},
+ {C_STRING_WITH_LEN("query_cache%")},
+ {C_STRING_WITH_LEN("secure_auth")},
+ {C_STRING_WITH_LEN("slow_launch_time")},
+ {C_STRING_WITH_LEN("sql%")},
+ {C_STRING_WITH_LEN("storage_engine")},
+ {C_STRING_WITH_LEN("sync_binlog")},
+ {C_STRING_WITH_LEN("table_definition_cache")},
+ {C_STRING_WITH_LEN("table_open_cache")},
+ {C_STRING_WITH_LEN("thread_handling")},
+ {C_STRING_WITH_LEN("time_zone")},
+ {C_STRING_WITH_LEN("timed_mutexes")},
+ {C_STRING_WITH_LEN("version%")},
+ {0, 0}
+};
+
+/**
+ Status variables that we want to see in the feedback report
+
+ (empty list = no WHERE condition)
+*/
+static LEX_STRING status_filter[]= {{0, 0}};
+
+/**
+ Fill our I_S table with data
+
+ This function works by invoking fill_variables() and
+ fill_status() of the corresponding I_S tables - to have
+ their data UNION-ed in the same target table.
+ After that it invokes our own fill_* functions
+ from the utils.cc - to get the data that aren't available in the
+ I_S.GLOBAL_VARIABLES and I_S.GLOBAL_STATUS.
+*/
+int fill_feedback(THD *thd, TABLE_LIST *tables, COND *unused)
+{
+ int res;
+ COND *cond;
+
+ tables->schema_table= schema_tables + SCH_GLOBAL_VARIABLES;
+ cond= make_cond(thd, tables, vars_filter);
+ res= (cond == OOM) ? 1 : fill_variables(thd, tables, cond);
+
+ tables->schema_table= schema_tables + SCH_GLOBAL_STATUS;
+ if (!res)
+ {
+ cond= make_cond(thd, tables, status_filter);
+ res= (cond == OOM) ? 1 : fill_status(thd, tables, cond);
+ }
+
+ tables->schema_table= i_s_feedback;
+ res= res || fill_plugin_version(thd, tables)
+ || fill_misc_data(thd, tables)
+ || fill_linux_info(thd, tables);
+
+ return res;
+}
+
+/**
+ plugin initialization function
+*/
+static int init(void *p)
+{
+ i_s_feedback= (ST_SCHEMA_TABLE*) p;
+ /* initialize the I_S descriptor structure */
+ i_s_feedback->fields_info= feedback_fields; ///< field descriptor
+ i_s_feedback->fill_table= fill_feedback; ///< how to fill the I_S table
+ i_s_feedback->idx_field1 = 0; ///< virtual index on the 1st col
+
+ if (calculate_server_uid(server_uid_buf))
+ return 1;
+
+ prepare_linux_info();
+
+ url_count= 0;
+ if (*url)
+ {
+ // now we split url on spaces and store them in Url objects
+ int slot;
+ char *s, *e;
+
+ for (s= url, url_count= 1; *s; s++)
+ if (*s == ' ')
+ url_count++;
+
+ urls= (Url **)my_malloc(url_count*sizeof(Url*), MYF(MY_WME));
+ if (!urls)
+ return 1;
+
+ for (s= url, e = url+1, slot= 0; e[-1]; e++)
+ if (*e == 0 || *e == ' ')
+ {
+ if (e > s && (urls[slot]= Url::create(s, e - s)))
+ slot++;
+ else
+ {
+ if (e > s)
+ sql_print_error("feedback plugin: invalid url '%.*s'", (int)(e-s), s);
+ url_count--;
+ }
+ s= e + 1;
+ }
+
+ // create a background thread to handle urls, if any
+ if (url_count)
+ {
+ pthread_mutex_init(&sleep_mutex, 0);
+ pthread_cond_init(&sleep_condition, 0);
+ shutdown_plugin= false;
+
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
+ if (pthread_create(&sender_thread, &attr, background_thread, 0) != 0)
+ {
+ sql_print_error("feedback plugin: failed to start a background thread");
+ return 1;
+ }
+ }
+ else
+ my_free(urls, MYF(0));
+ }
+
+ return 0;
+}
+
+/**
+ plugin deinitialization function
+*/
+static int free(void *p)
+{
+ if (url_count)
+ {
+ pthread_mutex_lock(&sleep_mutex);
+ shutdown_plugin= true;
+ pthread_cond_signal(&sleep_condition);
+ pthread_mutex_unlock(&sleep_mutex);
+ pthread_join(sender_thread, NULL);
+
+ pthread_mutex_destroy(&sleep_mutex);
+ pthread_cond_destroy(&sleep_condition);
+
+ for (uint i= 0; i < url_count; i++)
+ delete urls[i];
+ my_free(urls, MYF(0));
+ }
+ return 0;
+}
+
+static MYSQL_SYSVAR_STR(server_uid, server_uid,
+ PLUGIN_VAR_READONLY | PLUGIN_VAR_NOCMDOPT,
+ "Automatically calculated server unique id hash.", NULL, NULL, 0);
+static MYSQL_SYSVAR_STR(user_info, user_info,
+ PLUGIN_VAR_READONLY | PLUGIN_VAR_RQCMDARG,
+ "User specified string that will be included in the feedback report.",
+ NULL, NULL, "");
+static MYSQL_SYSVAR_STR(url, url, PLUGIN_VAR_READONLY | PLUGIN_VAR_RQCMDARG,
+ "Space separated URLs to send the feedback report to.", NULL, NULL,
+ "https://mariadb.org/feedback_plugin/post");
+static MYSQL_SYSVAR_ULONG(send_timeout, send_timeout, PLUGIN_VAR_RQCMDARG,
+ "Timeout (in seconds) for the sending the report.",
+ NULL, NULL, 60, 1, 60*60*24, 1);
+static MYSQL_SYSVAR_ULONG(send_retry_wait, send_retry_wait, PLUGIN_VAR_RQCMDARG,
+ "Wait this many seconds before retrying a failed send.",
+ NULL, NULL, 60, 1, 60*60*24, 1);
+
+static struct st_mysql_sys_var* settings[] = {
+ MYSQL_SYSVAR(server_uid),
+ MYSQL_SYSVAR(user_info),
+ MYSQL_SYSVAR(url),
+ MYSQL_SYSVAR(send_timeout),
+ MYSQL_SYSVAR(send_retry_wait),
+ NULL
+};
+
+
+static struct st_mysql_information_schema feedback =
+{ MYSQL_INFORMATION_SCHEMA_INTERFACE_VERSION };
+
+} // namespace feedback
+
+mysql_declare_plugin(feedback)
+{
+ MYSQL_INFORMATION_SCHEMA_PLUGIN,
+ &feedback::feedback,
+ "FEEDBACK",
+ "Sergei Golubchik",
+ "MariaDB User Feedback Plugin",
+ PLUGIN_LICENSE_GPL,
+ feedback::init,
+ feedback::free,
+ 0x0100,
+ NULL,
+ feedback::settings,
+ NULL
+}
+mysql_declare_plugin_end;
+
diff --git a/feedback.h b/feedback.h
new file mode 100644
index 00000000000..60ef72eed84
--- /dev/null
+++ b/feedback.h
@@ -0,0 +1,67 @@
+/* Copyright (C) 2010 Sergei Golubchik and Monty Program Ab
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ 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 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#define MYSQL_SERVER
+#include <mysql_priv.h>
+
+namespace feedback {
+
+int fill_feedback(THD *thd, TABLE_LIST *tables, COND *cond);
+int fill_plugin_version(THD *thd, TABLE_LIST *tables);
+int fill_misc_data(THD *thd, TABLE_LIST *tables);
+int fill_linux_info(THD *thd, TABLE_LIST *tables);
+
+static const int SERVER_UID_SIZE= 29;
+extern char server_uid_buf[SERVER_UID_SIZE+1], *user_info;
+int calculate_server_uid(char *);
+int prepare_linux_info();
+
+extern ST_SCHEMA_TABLE *i_s_feedback;
+
+extern ulong send_timeout, send_retry_wait;
+
+pthread_handler_t background_thread(void *arg);
+
+/**
+ The class for storing urls to send report data to.
+
+ Constructors are private, the object should be created with create() method.
+ send() method does the actual sending.
+*/
+class Url {
+ protected:
+ Url(LEX_STRING &url_arg) : full_url(url_arg) {}
+ const LEX_STRING full_url;
+
+ public:
+ virtual ~Url() { my_free(full_url.str, MYF(0)); }
+
+ const char *url() { return full_url.str; }
+ size_t url_length() { return full_url.length; }
+ virtual int send(const char* data, size_t data_length) = 0;
+
+ static Url* create(const char *url, size_t url_length);
+};
+
+extern Url **urls;
+extern uint url_count;
+
+/* these are used to communicate with the background thread */
+extern pthread_mutex_t sleep_mutex;
+extern pthread_cond_t sleep_condition;
+extern volatile bool shutdown_plugin;
+
+} // namespace feedback
+
diff --git a/plug.in b/plug.in
new file mode 100644
index 00000000000..0c93fef2327
--- /dev/null
+++ b/plug.in
@@ -0,0 +1,19 @@
+MYSQL_PLUGIN(feedback,[MariaDB User Feedback Plugin],
+ [MariaDB User Feedback Plugin])
+
+dnl Although it's not exactly obvious, top-level CMakeLists.txt parses plug.in
+dnl files, in particular looking for what the library name should be. It uses
+dnl regexp that matches MYSQL_PLUGIN_DYNAMIC or MYSQL_PLUGIN_STATIC, followed
+dnl by an open parenthesys, and the plugin name. Having engine name enclosed in
+dnl square brackets below causes this regexp to fail and as a result feedback
+dnl plugin will not be considered for dynamic builds on Windows.
+dnl Unfortunately, feedback cannot be built dynamically on Windows, because it
+dnl needs to access server internals that aren't designed for plugin use and
+dnl aren't marked with MYSQL_PLUGIN_IMPORT.
+MYSQL_PLUGIN_DYNAMIC([feedback], [feedback.la])
+MYSQL_PLUGIN_STATIC(feedback, [libfeedback.a])
+
+MYSQL_PLUGIN_ACTIONS(feedback, [
+ AC_CHECK_HEADERS([netdb.h sys/utsname.h])
+])
+
diff --git a/sender_thread.cc b/sender_thread.cc
new file mode 100644
index 00000000000..813690b8819
--- /dev/null
+++ b/sender_thread.cc
@@ -0,0 +1,294 @@
+/* Copyright (C) 2010 Sergei Golubchik and Monty Program Ab
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ 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 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include "feedback.h"
+#include <time.h>
+
+namespace feedback {
+
+static THD *thd= 0; ///< background thread thd
+static my_thread_id thd_thread_id; ///< its thread_id
+
+static size_t needed_size= 20480;
+
+static const time_t next_interval= 60*60*24*7; ///< in seconds (one week)
+static const time_t first_interval= 60*60*24; ///< in seconds (one day)
+
+/**
+ reads the rows from a table and puts them, concatenated, in a String
+
+ @note
+ 1. only supports two column tables - no less, no more.
+ 2. it emulates mysql -e "select * from..." and thus it separates
+ columns with \t and starts the output with column names.
+*/
+static int table_to_string(TABLE *table, String *result)
+{
+ bool res;
+ char buff1[MAX_FIELD_WIDTH], buff2[MAX_FIELD_WIDTH];
+ String str1(buff1, sizeof(buff1), system_charset_info);
+ String str2(buff2, sizeof(buff2), system_charset_info);
+
+ res= table->file->ha_rnd_init(1);
+
+ dbug_tmp_use_all_columns(table, table->read_set);
+
+ while(!res && !table->file->rnd_next(table->record[0]))
+ {
+ table->field[0]->val_str(&str1);
+ table->field[1]->val_str(&str2);
+ if (result->reserve(str1.length() + str2.length() + 3))
+ res= 1;
+ else
+ {
+ result->qs_append(str1.ptr(), str1.length());
+ result->qs_append('\t');
+ result->qs_append(str2.ptr(), str2.length());
+ result->qs_append('\n');
+ }
+ }
+
+ res = res || result->append('\n');
+
+ /*
+ Note, "|=" and not "||" - because we want to call ha_rnd_end()
+ even if res is already 1.
+ */
+ res |= table->file->ha_rnd_end();
+
+ return res;
+}
+
+/**
+ Initialize the THD and TABLE_LIST
+
+ The structures must be sufficiently initialized for create_tmp_table()
+ and fill_feedback() to work.
+*/
+static int prepare_for_fill(TABLE_LIST *tables)
+{
+ /*
+ Add our thd to the list, for it to be visible in SHOW PROCESSLIST.
+ But don't generate thread_id every time - use the saved value
+ (every increment of global thread_id counts as a new connection
+ in SHOW STATUS and we want to avoid skewing the statistics)
+ */
+ thd->thread_id= thd->variables.pseudo_thread_id= thd_thread_id;
+ pthread_mutex_lock(&LOCK_thread_count);
+ thread_count++;
+ threads.append(thd);
+ pthread_mutex_unlock(&LOCK_thread_count);
+ thd->thread_stack= (char*) &tables;
+ if (thd->store_globals())
+ return 1;
+
+ thd->mysys_var->current_cond= &sleep_condition;
+ thd->mysys_var->current_mutex= &sleep_mutex;
+ thd->proc_info="feedback";
+ thd->command=COM_SLEEP;
+ thd->version=refresh_version;
+ thd->system_thread= SYSTEM_THREAD_EVENT_WORKER; // whatever
+ thd->set_time();
+ thd->init_for_queries();
+ thd->real_id= pthread_self();
+ thd->db= NULL;
+ thd->db_length= 0;
+ thd->security_ctx->host_or_ip= "";
+ thd->security_ctx->db_access= DB_ACLS;
+ thd->security_ctx->master_access= ~NO_ACCESS;
+ bzero((char*) &thd->net, sizeof(thd->net));
+ lex_start(thd);
+ mysql_init_select(thd->lex);
+
+ tables->init_one_table(INFORMATION_SCHEMA_NAME.str,
+ i_s_feedback->table_name, TL_READ);
+ tables->schema_table= i_s_feedback;
+ tables->table= i_s_feedback->create_table(thd, tables);
+ if (!tables->table)
+ return 1;
+
+ tables->table->pos_in_table_list= tables;
+
+ return 0;
+}
+
+/**
+ Try to detect if this thread is going down
+
+ which can happen for different reasons:
+ * plugin is being unloaded
+ * mysqld server is being shut down
+ * the thread is being killed
+
+*/
+static bool going_down()
+{
+ return shutdown_plugin || shutdown_in_progress || (thd && thd->killed);
+}
+
+/**
+ just like sleep, but waits on a condition and checks "plugin shutdown" status
+*/
+static int delay(time_t sec)
+{
+ struct timespec abstime;
+ int ret= 0;
+
+ set_timespec(abstime, sec);
+
+ pthread_mutex_lock(&sleep_mutex);
+ while (!going_down() && ret != ETIMEDOUT)
+ ret= pthread_cond_timedwait(&sleep_condition, &sleep_mutex, &abstime);
+ pthread_mutex_unlock(&sleep_mutex);
+
+ return going_down();
+}
+
+/**
+ create a feedback report and send it to all specified urls
+
+ If "when" argument is not null, only it and the server uid are sent.
+ Otherwise a full report is generated.
+*/
+static void send_report(const char *when)
+{
+ TABLE_LIST tables;
+ String str;
+ int i, last_todo;
+ Url **todo= (Url**)alloca(url_count*sizeof(Url*));
+
+ str.alloc(needed_size); // preallocate it to avoid many small mallocs
+
+ /*
+ on startup and shutdown the server may not be completely
+ initialized, and full report won't work.
+ We send a short status notice only.
+ */
+ if (when)
+ {
+ str.length(0);
+ str.append(STRING_WITH_LEN("FEEDBACK_SERVER_UID"));
+ str.append('\t');
+ str.append(server_uid_buf);
+ str.append('\n');
+ str.append(STRING_WITH_LEN("FEEDBACK_WHEN"));
+ str.append('\t');
+ str.append(when);
+ str.append('\n');
+ str.append(STRING_WITH_LEN("FEEDBACK_USER_INFO"));
+ str.append('\t');
+ str.append(user_info);
+ str.append('\n');
+ str.append('\n');
+ }
+ else
+ {
+ /*
+ otherwise, prepare the THD and TABLE_LIST,
+ create and fill the temporary table with data just like
+ SELECT * FROM IFROEMATION_SCHEMA.feedback is doing,
+ read and concatenate table data into a String.
+ */
+ if (!(thd= new THD()))
+ return;
+
+ if (prepare_for_fill(&tables))
+ goto ret;
+
+ if (fill_feedback(thd, &tables, NULL))
+ goto ret;
+
+ if (table_to_string(tables.table, &str))
+ goto ret;
+
+ needed_size= (size_t)(str.length() * 1.1);
+
+ free_tmp_table(thd, tables.table);
+ tables.table= 0;
+ }
+
+ /*
+ Try to send the report on every url from the list, remove url on success,
+ keep failed in the list. Repeat until the list is empty.
+ */
+ memcpy(todo, urls, url_count*sizeof(Url*));
+ last_todo= url_count - 1;
+ do
+ {
+ for (i= 0; i <= last_todo;)
+ {
+ Url *url= todo[i];
+
+ if (thd) // for nicer SHOW PROCESSLIST
+ thd->set_query(const_cast<char*>(url->url()), url->url_length());
+
+ if (url->send(str.ptr(), str.length()))
+ i++;
+ else
+ todo[i]= todo[last_todo--];
+ }
+ if (last_todo < 0)
+ break;
+ } while (delay(send_retry_wait) == 0); // wait a little bit before retrying
+
+ret:
+ if (thd)
+ {
+ if (tables.table)
+ free_tmp_table(thd, tables.table);
+ /*
+ clean up, free the thd.
+ reset all thread local status variables to minimize
+ the effect of the background thread on SHOW STATUS.
+ */
+ pthread_mutex_lock(&LOCK_thread_count);
+ bzero(&thd->status_var, sizeof(thd->status_var));
+ thread_count--;
+ thd->killed= THD::KILL_CONNECTION;
+ pthread_cond_broadcast(&COND_thread_count);
+ pthread_mutex_unlock(&LOCK_thread_count);
+ delete thd;
+ thd= 0;
+ }
+}
+
+/**
+ background sending thread
+*/
+pthread_handler_t background_thread(void *arg __attribute__((unused)))
+{
+ time_t interval;
+
+ if (my_thread_init())
+ return 0;
+
+ pthread_mutex_lock(&LOCK_thread_count);
+ thd_thread_id= thread_id++;
+ pthread_mutex_unlock(&LOCK_thread_count);
+
+ send_report("startup");
+
+ for (interval= first_interval; delay(interval) == 0; interval= next_interval)
+ send_report(NULL);
+
+ send_report("shutdown");
+
+ my_thread_end();
+ pthread_exit(0);
+ return 0;
+}
+
+} // namespace feedback
+
diff --git a/url_base.cc b/url_base.cc
new file mode 100644
index 00000000000..38b2ca86e9f
--- /dev/null
+++ b/url_base.cc
@@ -0,0 +1,51 @@
+/* Copyright (C) 2010 Sergei Golubchik and Monty Program Ab
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ 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 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include "feedback.h"
+
+namespace feedback {
+
+Url* http_create(const char *url, size_t url_length);
+
+/**
+ creates an Url object out of an url, if possible.
+
+ This is done by invoking corresponding creator functions
+ of the derived classes, until the first not NULL result.
+*/
+Url* Url::create(const char *url, size_t url_length)
+{
+ url= my_strndup(url, url_length, MYF(MY_WME));
+
+ if (!url)
+ return NULL;
+
+ Url *self= http_create(url, url_length);
+
+ /*
+ here we can add
+
+ if (!self) self= smtp_create(url, url_length);
+ if (!self) self= tftp_create(url, url_length);
+ etc
+ */
+
+ if (!self)
+ my_free(const_cast<char*>(url), MYF(0));
+
+ return self;
+}
+
+} // namespace feedback
diff --git a/url_http.cc b/url_http.cc
new file mode 100644
index 00000000000..efe987327d3
--- /dev/null
+++ b/url_http.cc
@@ -0,0 +1,308 @@
+/* Copyright (C) 2010 Sergei Golubchik and Monty Program Ab
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ 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 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include "feedback.h"
+
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+
+#ifdef _WIN32
+#undef VOID
+#define VOID void
+#include <ws2tcpip.h>
+#define addrinfo ADDRINFOA
+#endif
+
+namespace feedback {
+
+static const char *http= "http://";
+static const size_t http_len= 7;
+static const char *https= "https://";
+static const size_t https_len= 8;
+
+static const uint FOR_READING= 0;
+static const uint FOR_WRITING= 1;
+
+#ifdef MARIADB_BASE_VERSION
+#define ssl_connect(A,B,C,D) sslconnect(A,B,C,D)
+#else
+#define ssl_connect(A,B,C,D) sslconnect(A,B,C)
+#endif
+
+/**
+ implementation of the Url class that sends the data via HTTP POST request.
+
+ Both http:// and https:// protocols are supported.
+*/
+class Url_http: public Url {
+ protected:
+ const LEX_STRING host, port, path;
+ bool ssl;
+
+ Url_http(LEX_STRING &url_arg, LEX_STRING &host_arg,
+ LEX_STRING &port_arg, LEX_STRING &path_arg, bool ssl_arg) :
+ Url(url_arg), host(host_arg), port(port_arg), path(path_arg), ssl(ssl_arg)
+ {}
+ ~Url_http()
+ {
+ my_free(host.str, MYF(0));
+ my_free(port.str, MYF(0));
+ my_free(path.str, MYF(0));
+ }
+
+ public:
+ int send(const char* data, size_t data_length);
+
+ friend Url* http_create(const char *url, size_t url_length);
+};
+
+/**
+ create a Url_http object out of the url, if possible.
+
+ @note
+ Arbitrary limitations here.
+
+ The url must be http[s]://hostname[:port]/path
+ No username:password@ or ?script=parameters are supported.
+
+ But it's ok. This is not a generic purpose www browser - it only needs to be
+ good enough to POST the data to mariadb.org.
+*/
+Url* http_create(const char *url, size_t url_length)
+{
+ const char *s;
+ LEX_STRING full_url= {const_cast<char*>(url), url_length};
+ LEX_STRING host, port, path;
+ bool ssl= false;
+
+ if (is_prefix(url, http))
+ s= url + http_len;
+#ifdef HAVE_OPENSSL
+ else if (is_prefix(url, https))
+ {
+ ssl= true;
+ s= url + https_len;
+ }
+#endif
+ else
+ return NULL;
+
+ for (url= s; *s && *s != ':' && *s != '/'; s++) /* no-op */;
+ host.str= const_cast<char*>(url);
+ host.length= s-url;
+
+ if (*s == ':')
+ {
+ for (url= ++s; *s && *s >= '0' && *s <= '9'; s++) /* no-op */;
+ port.str= const_cast<char*>(url);
+ port.length= s-url;
+ }
+ else
+ {
+ if (ssl)
+ {
+ port.str= const_cast<char*>("443");
+ port.length=3;
+ }
+ else
+ {
+ port.str= const_cast<char*>("80");
+ port.length=2;
+ }
+ }
+
+ if (*s == 0)
+ {
+ path.str= const_cast<char*>("/");
+ path.length= 1;
+ }
+ else
+ {
+ path.str= const_cast<char*>(s);
+ path.length= strlen(s);
+ }
+ if (!host.length || !port.length || path.str[0] != '/')
+ return NULL;
+
+ host.str= my_strndup(host.str, host.length, MYF(MY_WME));
+ port.str= my_strndup(port.str, port.length, MYF(MY_WME));
+ path.str= my_strndup(path.str, path.length, MYF(MY_WME));
+
+ if (!host.str || !port.str || !path.str)
+ {
+ my_free(host.str, MYF(MY_ALLOW_ZERO_PTR));
+ my_free(port.str, MYF(MY_ALLOW_ZERO_PTR));
+ my_free(path.str, MYF(MY_ALLOW_ZERO_PTR));
+ return NULL;
+ }
+
+ return new Url_http(full_url, host, port, path, ssl);
+}
+
+/* do the vio_write and check that all data were sent ok */
+#define write_check(VIO, DATA, LEN) \
+ (vio_write((VIO), (uchar*)(DATA), (LEN)) != (LEN))
+
+int Url_http::send(const char* data, size_t data_length)
+{
+ my_socket fd= INVALID_SOCKET;
+ char buf[1024];
+ uint len;
+
+ addrinfo *addrs, *addr, filter= {0, AF_UNSPEC, SOCK_STREAM, 6, 0, 0, 0, 0};
+ int res= getaddrinfo(host.str, port.str, &filter, &addrs);
+
+ if (res)
+ {
+ sql_print_error("feedback plugin: getaddrinfo() failed for url '%s': %s",
+ full_url.str, gai_strerror(res));
+ return 1;
+ }
+
+ for (addr= addrs; addr != NULL; addr= addr->ai_next)
+ {
+ fd= socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
+ if (fd == INVALID_SOCKET)
+ continue;
+
+ if (connect(fd, addr->ai_addr, addr->ai_addrlen) == 0)
+ break;
+
+ closesocket(fd);
+ }
+
+ freeaddrinfo(addrs);
+
+ if (fd == INVALID_SOCKET)
+ {
+ sql_print_error("feedback plugin: could not connect for url '%s'",
+ full_url.str);
+ return 1;
+ }
+
+ Vio *vio= vio_new(fd, VIO_TYPE_TCPIP, 0);
+ if (!vio)
+ {
+ sql_print_error("feedback plugin: vio_new failed for url '%s'",
+ full_url.str);
+ closesocket(fd);
+ return 1;
+ }
+
+#ifdef HAVE_OPENSSL
+ struct st_VioSSLFd *ssl_fd;
+ if (ssl)
+ {
+ buf[0]= 0;
+ if (!(ssl_fd= new_VioSSLConnectorFd(0, 0, 0, 0, 0)) ||
+ ssl_connect(ssl_fd, vio, send_timeout, buf))
+ {
+ sql_print_error("feedback plugin: ssl failed for url '%s' %s",
+ full_url.str, buf);
+ if (ssl_fd)
+ free_vio_ssl_acceptor_fd(ssl_fd);
+ closesocket(fd);
+ vio_delete(vio);
+ return 1;
+ }
+ }
+#endif
+
+ static const LEX_STRING boundary=
+ { C_STRING_WITH_LEN("----------------------------ba4f3696b39f") };
+ static const LEX_STRING header=
+ { C_STRING_WITH_LEN("\r\n"
+ "Content-Disposition: form-data; name=\"data\"; filename=\"-\"\r\n"
+ "Content-Type: application/octet-stream\r\n\r\n")
+ };
+
+ len= my_snprintf(buf, sizeof(buf),
+ "POST %s HTTP/1.0\r\n"
+ "User-Agent: MariaDB User Feedback Plugin\r\n"
+ "Host: %s:%s\r\n"
+ "Accept: */*\r\n"
+ "Content-Length: %u\r\n"
+ "Content-Type: multipart/form-data; boundary=%s\r\n"
+ "\r\n",
+ path.str, host.str, port.str,
+ (uint)(2*boundary.length + header.length + data_length + 4),
+ boundary.str + 2);
+
+ vio_timeout(vio, FOR_READING, send_timeout);
+ vio_timeout(vio, FOR_WRITING, send_timeout);
+ res = write_check(vio, buf, len)
+ || write_check(vio, boundary.str, boundary.length)
+ || write_check(vio, header.str, header.length)
+ || write_check(vio, data, data_length)
+ || write_check(vio, boundary.str, boundary.length)
+ || write_check(vio, "--\r\n", 4);
+
+ if (res)
+ sql_print_error("feedback plugin: failed to send report to '%s'",
+ full_url.str);
+ else
+ {
+ sql_print_information("feedback plugin: report to '%s' was sent",
+ full_url.str);
+
+ /*
+ if the data were send successfully, read the reply.
+ Extract the first string between <h1>...</h1> tags
+ and put it as a server reply into the error log.
+ */
+ len= vio_read(vio, (uchar*)buf, sizeof(buf)-1);
+ if (len && len < sizeof(buf))
+ {
+ char *from;
+
+ buf[len+1]= 0; // safety
+
+ if ((from= strstr(buf, "<h1>")))
+ {
+ from+= 4;
+ char *to= strstr(from, "</h1>");
+ if (to)
+ *to= 0;
+ else
+ from= NULL;
+ }
+ if (from)
+ sql_print_information("feedback plugin: server replied '%s'", from);
+ else
+ sql_print_warning("feedback plugin: failed to parse server reply");
+ }
+ else
+ {
+ res= 1;
+ sql_print_error("feedback plugin: failed to read server reply");
+ }
+ }
+
+ vio_delete(vio);
+
+#ifdef HAVE_OPENSSL
+ if (ssl)
+ {
+ SSL_CTX_free(ssl_fd->ssl_context);
+ my_free(ssl_fd, MYF(0));
+ }
+#endif
+
+ return res;
+}
+
+} // namespace feedback
+
diff --git a/utils.cc b/utils.cc
new file mode 100644
index 00000000000..f5afd427ebf
--- /dev/null
+++ b/utils.cc
@@ -0,0 +1,299 @@
+/* Copyright (C) 2010 Sergei Golubchik and Monty Program Ab
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ 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 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include "feedback.h"
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <base64.h>
+#include <sha1.h>
+
+#ifdef HAVE_SYS_UTSNAME_H
+#include <sys/utsname.h>
+static bool have_ubuf= false;
+static struct utsname ubuf;
+#endif
+
+#ifdef TARGET_OS_LINUX
+#include <glob.h>
+static bool have_distribution= false;
+static char distribution[256];
+
+static const char *masks[]= {
+ "/etc/*-version", "/etc/*-release",
+ "/etc/*_version", "/etc/*_release"
+};
+#endif
+
+bool schema_table_store_record(THD *thd, TABLE *table);
+
+namespace feedback {
+
+/*
+ convenience macros for inserting rows into I_S table.
+*/
+#define INSERT2(NAME,LEN,VALUE) \
+ do { \
+ table->field[0]->store(NAME, LEN, system_charset_info); \
+ table->field[1]->store VALUE; \
+ if (schema_table_store_record(thd, table)) \
+ return 1; \
+ } while (0)
+
+#define INSERT1(NAME,VALUE) \
+ do { \
+ table->field[0]->store(NAME, sizeof(NAME)-1, system_charset_info); \
+ table->field[1]->store VALUE; \
+ if (schema_table_store_record(thd, table)) \
+ return 1; \
+ } while (0)
+
+static const bool UNSIGNED= true; ///< used below when inserting integers
+
+/**
+ callback for fill_plugin_version() - insert a plugin name and its version
+*/
+static my_bool show_plugins(THD *thd, plugin_ref plugin, void *arg)
+{
+ TABLE *table= (TABLE*) arg;
+ char version[20];
+ size_t version_len;
+
+ version_len= my_snprintf(version, sizeof(version), "%d.%d",
+ (plugin_decl(plugin)->version) >> 8,
+ (plugin_decl(plugin)->version) & 0xff);
+
+ INSERT2(plugin_name(plugin)->str, plugin_name(plugin)->length,
+ (version, version_len, system_charset_info));
+
+ return 0;
+}
+
+/**
+ inserts all plugins and their versions into I_S.FEEDBACK
+*/
+int fill_plugin_version(THD *thd, TABLE_LIST *tables)
+{
+ return plugin_foreach_with_mask(thd, show_plugins, MYSQL_ANY_PLUGIN,
+ ~PLUGIN_IS_FREED, tables->table);
+}
+
+#if defined(_SC_PAGE_SIZE) && !defined(_SC_PAGESIZE)
+#define _SC_PAGESIZE _SC_PAGE_SIZE
+#endif
+
+/**
+ return the amount of physical memory
+*/
+static ulonglong my_getphysmem()
+{
+ ulonglong pages= 0;
+#ifdef _SC_PHYS_PAGES
+ pages= sysconf(_SC_PHYS_PAGES);
+#else
+ return 0;
+#endif
+
+#ifdef _SC_PAGESIZE
+ return pages * sysconf(_SC_PAGESIZE);
+#else
+ return pages * my_getpagesize();
+#endif
+}
+
+/* get the number of (online) CPUs */
+int my_getncpus()
+{
+#ifdef _SC_NPROCESSORS_ONLN
+ return sysconf(_SC_NPROCESSORS_ONLN);
+#elif defined(__WIN__)
+ SYSTEM_INFO sysinfo;
+ GetSystemInfo(&sysinfo);
+ return sysinfo.dwNumberOfProcessors;
+#else
+ return 0;
+#endif
+}
+
+/**
+ Find the version of the kernel and the linux distribution
+*/
+int prepare_linux_info()
+{
+#ifdef HAVE_SYS_UTSNAME_H
+ have_ubuf= (uname(&ubuf) != -1);
+#endif
+
+#ifdef TARGET_OS_LINUX
+ /*
+ let's try to find what linux distribution it is
+ we read *[-_]{release,version} file in /etc.
+
+ Either it will be /etc/lsb-release, such as
+
+ ==> /etc/lsb-release <==
+ DISTRIB_ID=Ubuntu
+ DISTRIB_RELEASE=8.04
+ DISTRIB_CODENAME=hardy
+ DISTRIB_DESCRIPTION="Ubuntu 8.04.4 LTS"
+
+ Or a one-liner with the description (/etc/SuSE-release has more
+ than one line, but the description is the first, so it can be
+ treated as a one-liner).
+
+ We'll read lsb-release first, and if it's not found will search
+ for other files (*-version *-release *_version *_release)
+*/
+ int fd;
+ have_distribution= false;
+ if ((fd= my_open("/etc/lsb-release", O_RDONLY, MYF(0))) != -1)
+ {
+ /* Cool, LSB-compliant distribution! */
+ size_t len= my_read(fd, (uchar*)distribution, sizeof(distribution)-1, MYF(0));
+ my_close(fd, MYF(0));
+ if (len != (size_t)-1)
+ {
+ distribution[len]= 0; // safety
+ char *found= strstr(distribution, "DISTRIB_DESCRIPTION=");
+ if (found)
+ {
+ have_distribution= true;
+ char *end= strstr(found, "\n");
+ if (end == NULL)
+ end= distribution + len;
+ found+= 20;
+
+ if (*found == '"' && end[-1] == '"')
+ {
+ found++;
+ end--;
+ }
+ *end= 0;
+
+ char *to= strmov(distribution, "lsb: ");
+ memmove(to, found, end - found + 1);
+ }
+ }
+ }
+
+ /* if not an LSB-compliant distribution */
+ for (uint i= 0; !have_distribution && i < array_elements(masks); i++)
+ {
+ glob_t found;
+ if (glob(masks[i], GLOB_NOSORT, NULL, &found) == 0)
+ {
+ int fd;
+ if ((fd= my_open(found.gl_pathv[0], O_RDONLY, MYF(0))) != -1)
+ {
+ /*
+ +5 and -8 below cut the file name part out of the
+ full pathname that corresponds to the mask as above.
+ */
+ char *to= strmov(distribution, found.gl_pathv[0] + 5) - 8;
+ *to++= ':';
+ *to++= ' ';
+
+ size_t to_len= distribution + sizeof(distribution) - 1 - to;
+ size_t len= my_read(fd, (uchar*)to, to_len, MYF(0));
+ my_close(fd, MYF(0));
+ if (len != (size_t)-1)
+ {
+ to[len]= 0; // safety
+ char *end= strstr(to, "\n");
+ if (end)
+ *end= 0;
+ have_distribution= true;
+ }
+ }
+ }
+ globfree(&found);
+ }
+#endif
+ return 0;
+}
+
+/**
+ Add the linux distribution and the kernel version
+*/
+int fill_linux_info(THD *thd, TABLE_LIST *tables)
+{
+ TABLE *table= tables->table;
+ CHARSET_INFO *cs= system_charset_info;
+
+#ifdef HAVE_SYS_UTSNAME_H
+ if (have_ubuf)
+ {
+ INSERT1("Uname_sysname", (ubuf.sysname, strlen(ubuf.sysname), cs));
+ INSERT1("Uname_release", (ubuf.release, strlen(ubuf.release), cs));
+ INSERT1("Uname_version", (ubuf.version, strlen(ubuf.version), cs));
+ INSERT1("Uname_machine", (ubuf.machine, strlen(ubuf.machine), cs));
+ }
+#endif
+
+#ifdef TARGET_OS_LINUX
+ if (have_distribution)
+ INSERT1("Uname_distribution", (distribution, strlen(distribution), cs));
+#endif
+
+ return 0;
+}
+
+/**
+ Adds varios bits of information to the I_S.FEEDBACK
+*/
+int fill_misc_data(THD *thd, TABLE_LIST *tables)
+{
+ TABLE *table= tables->table;
+
+#ifdef MY_ATOMIC_OK
+ INSERT1("Cpu_count", (my_getncpus(), UNSIGNED));
+#endif
+ INSERT1("Mem_total", (my_getphysmem(), UNSIGNED));
+
+ return 0;
+}
+
+/**
+ calculates the server unique identifier
+
+ UID is a base64 encoded SHA1 hash of the MAC address of one of
+ the interfaces, and the tcp port that the server is listening on
+*/
+int calculate_server_uid(char *dest)
+{
+ uchar rawbuf[2 + 6];
+ uchar shabuf[SHA1_HASH_SIZE];
+ SHA1_CONTEXT ctx;
+
+ int2store(rawbuf, mysqld_port);
+ if (my_gethwaddr(rawbuf + 2))
+ {
+ sql_print_error("feedback plugin: failed to retrieve the MAC address");
+ return 1;
+ }
+
+ mysql_sha1_reset(&ctx);
+ mysql_sha1_input(&ctx, rawbuf, sizeof(rawbuf));
+ mysql_sha1_result(&ctx, shabuf);
+
+ assert(base64_needed_encoded_length(sizeof(shabuf)) <= SERVER_UID_SIZE);
+ base64_encode(shabuf, sizeof(shabuf), dest);
+
+ return 0;
+}
+
+} // namespace feedback