summaryrefslogtreecommitdiffstats
path: root/libmariadb/plugins/io/remote_io.c
diff options
context:
space:
mode:
Diffstat (limited to 'libmariadb/plugins/io/remote_io.c')
-rw-r--r--libmariadb/plugins/io/remote_io.c453
1 files changed, 453 insertions, 0 deletions
diff --git a/libmariadb/plugins/io/remote_io.c b/libmariadb/plugins/io/remote_io.c
new file mode 100644
index 00000000..c06ecacd
--- /dev/null
+++ b/libmariadb/plugins/io/remote_io.c
@@ -0,0 +1,453 @@
+/************************************************************************************
+ * Copyright (C) 2015 - 2018 MariaDB Corporation AB
+ * Copyright (c) 2003 Simtec Electronics
+ *
+ * Re-implemented by Vincent Sanders <vince@kyllikki.org> with extensive
+ * reference to original curl example code
+ *
+ * Rewritten for MariaDB Connector/C by Georg Richter <georg@mariadb.com>
+ *
+ * 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. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 is a plugin for remote file access via libcurl.
+
+ The following URL types are supported:
+
+ http://
+ https://
+ ftp://
+ sftp://
+ ldap://
+ smb://
+*/
+
+#include <ma_global.h>
+#include <ma_sys.h>
+#include <mysql.h>
+#include <mysql/client_plugin.h>
+#include <string.h>
+#include <memory.h>
+
+#include <stdio.h>
+#include <string.h>
+#ifndef WIN32
+#include <sys/time.h>
+#else
+#pragma comment(lib, "Ws2_32.lib")
+#endif
+#include <stdlib.h>
+#include <errno.h>
+#include <mariadb/ma_io.h>
+
+/* Internal file structure */
+
+MA_FILE *ma_rio_open(const char *url,const char *operation);
+int ma_rio_close(MA_FILE *file);
+int ma_rio_feof(MA_FILE *file);
+size_t ma_rio_read(void *ptr, size_t size, size_t nmemb, MA_FILE *file);
+char * ma_rio_gets(char *ptr, size_t size, MA_FILE *file);
+
+int ma_rio_init(char *, size_t, int, va_list);
+int ma_rio_deinit(void);
+
+struct st_rio_methods ma_rio_methods= {
+ ma_rio_open,
+ ma_rio_close,
+ ma_rio_feof,
+ ma_rio_read,
+ ma_rio_gets
+};
+
+typedef struct
+{
+ CURL *curl;
+ size_t length,
+ offset;
+ uchar *buffer;
+ int in_progress;
+} MA_REMOTE_FILE;
+
+CURLM *multi_handle= NULL;
+
+#ifndef PLUGIN_DYNAMIC
+MARIADB_REMOTEIO_PLUGIN remote_io_client_plugin=
+#else
+MARIADB_REMOTEIO_PLUGIN _mysql_client_plugin_declaration_ =
+#endif
+{
+ MARIADB_CLIENT_REMOTEIO_PLUGIN,
+ MARIADB_CLIENT_REMOTEIO_PLUGIN_INTERFACE_VERSION,
+ "remote_io",
+ "Georg Richter",
+ "Remote IO plugin",
+ {0,1,0},
+ "LGPL",
+ NULL,
+ ma_rio_init,
+ ma_rio_deinit,
+ NULL,
+ &ma_rio_methods
+mysql_end_client_plugin;
+
+/* {{{ ma_rio_init - Plugin initialization */
+int ma_rio_init(char *unused1 __attribute__((unused)),
+ size_t unused2 __attribute__((unused)),
+ int unused3 __attribute__((unused)),
+ va_list unused4 __attribute__((unused)))
+{
+ curl_global_init(CURL_GLOBAL_ALL);
+ if (!multi_handle)
+ multi_handle = curl_multi_init();
+ return 0;
+}
+/* }}} */
+
+/* {{{ ma_rio_deinit - Plugin deinitialization */
+int ma_rio_deinit(void)
+{
+ if (multi_handle)
+ {
+ curl_multi_cleanup(multi_handle);
+ multi_handle= NULL;
+ }
+ curl_global_cleanup();
+ return 0;
+}
+/* }}} */
+
+/* {{{ curl_write_callback */
+static size_t rio_write_callback(char *buffer,
+ size_t size,
+ size_t nitems,
+ void *ptr)
+{
+ size_t free_bytes;
+ char *tmp;
+
+ MA_FILE *file= (MA_FILE *)ptr;
+ MA_REMOTE_FILE *curl_file = (MA_REMOTE_FILE *)file->ptr;
+ size *= nitems;
+
+ free_bytes= curl_file->length - curl_file->offset;
+
+ /* check if we need to allocate more memory */
+ if (size > free_bytes) {
+ tmp= (char *)realloc((gptr)curl_file->buffer, curl_file->length + (size - free_bytes));
+ if (!tmp)
+ size= free_bytes;
+ else {
+ curl_file->length+= size - free_bytes;
+ curl_file->buffer= (unsigned char *)tmp;
+ }
+ }
+
+ /* copy buffer into MA_FILE structure */
+ memcpy((char *)curl_file->buffer + curl_file->offset, buffer, size);
+ curl_file->offset+= size;
+
+ return size;
+}
+/* }}} */
+
+/* use to attempt to fill the read buffer up to requested number of bytes */
+static int fill_buffer(MA_FILE *file, size_t want)
+{
+ fd_set fdread;
+ fd_set fdwrite;
+ fd_set fdexcep;
+ struct timeval timeout;
+ int rc;
+ CURLMcode mc; /* curl_multi_fdset() return code */
+ MA_REMOTE_FILE *rf= (MA_REMOTE_FILE *)file->ptr;
+
+ /* only attempt to fill buffer if transactions still running and buffer
+ doesn't exceed required size already */
+ if (!rf->in_progress || (rf->offset > want))
+ return 0;
+
+ /* try to fill buffer */
+ do {
+ int maxfd = -1;
+ long curl_timeo = -1;
+
+ FD_ZERO(&fdread);
+ FD_ZERO(&fdwrite);
+ FD_ZERO(&fdexcep);
+
+ /* set a suitable timeout to fail on */
+ timeout.tv_sec = 20; /* 20 seconds */
+ timeout.tv_usec = 0;
+
+ curl_multi_timeout(multi_handle, &curl_timeo);
+ if(curl_timeo >= 0) {
+ timeout.tv_sec = curl_timeo / 1000;
+ if(timeout.tv_sec > 1)
+ timeout.tv_sec = 1;
+ else
+ timeout.tv_usec = (curl_timeo % 1000) * 1000;
+ }
+
+ /* get file descriptors from the transfers */
+ mc = curl_multi_fdset(multi_handle, &fdread, &fdwrite, &fdexcep, &maxfd);
+
+ if(mc != CURLM_OK)
+ {
+ /* todo: error handling */
+ break;
+ }
+
+ /* On success the value of maxfd is guaranteed to be >= -1. We call
+ select(maxfd + 1, ...); specially in case of (maxfd == -1) there are
+ no fds ready yet so we call select(0, ...) */
+
+ if(maxfd == -1) {
+ struct timeval wait = { 0, 100 * 1000 }; /* 100ms */
+ rc = select(0, NULL, NULL, NULL, &wait);
+ }
+ else {
+ rc = select(maxfd+1, &fdread, &fdwrite, &fdexcep, &timeout);
+ }
+
+ switch(rc) {
+ case -1:
+ /* select error */
+ break;
+
+ case 0:
+ default:
+ /* timeout or readable/writable sockets */
+ curl_multi_perform(multi_handle, &rf->in_progress);
+ break;
+ }
+ } while(rf->in_progress && (rf->offset < want));
+ return 1;
+}
+
+/* use to remove want bytes from the front of a files buffer */
+static int use_buffer(MA_FILE *file,int want)
+{
+ MA_REMOTE_FILE *rf= (MA_REMOTE_FILE *)file->ptr;
+ /* sort out buffer */
+ if((rf->offset - want) <=0) {
+ /* ditch buffer - write will recreate */
+ if (rf->buffer)
+ free(rf->buffer);
+
+ rf->buffer=NULL;
+ rf->offset=0;
+ rf->length=0;
+ }
+ else {
+ /* move rest down make it available for later */
+ memmove(rf->buffer,
+ &rf->buffer[want],
+ (rf->offset - want));
+
+ rf->offset -= want;
+ }
+ return 0;
+}
+
+MA_FILE *ma_rio_open(const char *url,const char *operation)
+{
+ /* this code could check for URLs or types in the 'url' and
+ basically use the real fopen() for standard files */
+
+ MA_FILE *file;
+ MA_REMOTE_FILE *rf;
+ (void)operation;
+
+ if (!(file = (MA_FILE *)calloc(sizeof(MA_FILE), 1)))
+ return NULL;
+
+ file->type= MA_FILE_REMOTE;
+ if (!(file->ptr= rf= (MA_REMOTE_FILE *)calloc(sizeof(MA_REMOTE_FILE), 1)))
+ {
+ free(file);
+ return NULL;
+ }
+ rf->curl = curl_easy_init();
+
+ if (curl_easy_setopt(rf->curl, CURLOPT_URL, url) ||
+ curl_easy_setopt(rf->curl, CURLOPT_WRITEDATA, file) ||
+ curl_easy_setopt(rf->curl, CURLOPT_VERBOSE, 0L) ||
+ curl_easy_setopt(rf->curl, CURLOPT_WRITEFUNCTION, rio_write_callback))
+ {
+ free(file);
+ free(rf);
+ return NULL;
+ }
+
+ curl_multi_add_handle(multi_handle, rf->curl);
+
+ /* lets start the fetch */
+ curl_multi_perform(multi_handle, &rf->in_progress);
+
+ if((rf->offset == 0) && (!rf->in_progress)) {
+ /* if in_progress is 0 now, we should return NULL */
+
+ /* make sure the easy handle is not in the multi handle anymore */
+ curl_multi_remove_handle(multi_handle, rf->curl);
+
+ /* cleanup */
+ curl_easy_cleanup(rf->curl);
+
+ free(file);
+
+ file = NULL;
+ }
+ return file;
+}
+
+int ma_rio_close(MA_FILE *file)
+{
+ int ret=0;/* default is good return */
+ MA_REMOTE_FILE *rf= (MA_REMOTE_FILE *)file->ptr;
+
+ switch(file->type) {
+ case MA_FILE_REMOTE:
+ curl_multi_remove_handle(multi_handle, rf->curl);
+
+ /* cleanup */
+ curl_easy_cleanup(rf->curl);
+ break;
+
+ default: /* unknown or supported type - oh dear */
+ ret=EOF;
+ errno=EBADF;
+ break;
+ }
+
+ if(rf->buffer)
+ free(rf->buffer);/* free any allocated buffer space */
+
+ free(rf);
+ free(file);
+
+ return ret;
+}
+
+int ma_rio_feof(MA_FILE *file)
+{
+ int ret=0;
+ MA_REMOTE_FILE *rf= (MA_REMOTE_FILE *)file->ptr;
+
+ switch(file->type) {
+ case MA_FILE_REMOTE:
+ if((rf->offset == 0) && (!rf->in_progress))
+ ret = 1;
+ break;
+
+ default: /* unknown or supported type - oh dear */
+ ret=-1;
+ errno=EBADF;
+ break;
+ }
+ return ret;
+}
+
+size_t ma_rio_read(void *ptr, size_t size, size_t nmemb, MA_FILE *file)
+{
+ size_t want;
+ MA_REMOTE_FILE *rf= (MA_REMOTE_FILE *)file->ptr;
+
+ switch(file->type) {
+ case MA_FILE_REMOTE:
+ want = nmemb * size;
+
+ fill_buffer(file,want);
+
+ /* check if there's data in the buffer - if not fill_buffer()
+ * either errored or EOF */
+ if(!rf->offset)
+ return 0;
+
+ /* ensure only available data is considered */
+ if(rf->offset < want)
+ want = rf->offset;
+
+ /* xfer data to caller */
+ memcpy(ptr, rf->buffer, want);
+
+ use_buffer(file,want);
+
+ want = want / size; /* number of items */
+ break;
+
+ default: /* unknown or supported type - oh dear */
+ want=0;
+ errno=EBADF;
+ break;
+
+ }
+ return want;
+}
+
+char *ma_rio_gets(char *ptr, size_t size, MA_FILE *file)
+{
+ size_t want = size - 1;/* always need to leave room for zero termination */
+ size_t loop;
+
+ switch(file->type) {
+ case MA_FILE_REMOTE:
+ {
+ MA_REMOTE_FILE *rf= (MA_REMOTE_FILE *)file->ptr;
+ fill_buffer(file,want);
+
+ /* check if there's data in the buffer - if not fill either errored or
+ * EOF */
+ if(!rf->offset)
+ return NULL;
+
+ /* ensure only available data is considered */
+ if(rf->offset < want)
+ want = rf->offset;
+
+ /*buffer contains data */
+ /* look for newline or eof */
+ for(loop=0;loop < want;loop++) {
+ if(rf->buffer[loop] == '\n') {
+ want=loop+1;/* include newline */
+ break;
+ }
+ }
+
+ /* xfer data to caller */
+ memcpy(ptr, rf->buffer, want);
+ ptr[want]=0;/* always null terminate */
+
+ use_buffer(file,want);
+
+ break;
+ }
+
+ default: /* unknown or supported type - oh dear */
+ ptr=NULL;
+ errno=EBADF;
+ break;
+ }
+
+ return ptr;/*success */
+}