summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source3/lib/asys/asys.c274
-rw-r--r--source3/lib/asys/asys.h146
-rw-r--r--source3/lib/asys/tests.c92
-rw-r--r--source3/lib/asys/wscript_build9
-rwxr-xr-xsource3/wscript_build1
5 files changed, 522 insertions, 0 deletions
diff --git a/source3/lib/asys/asys.c b/source3/lib/asys/asys.c
new file mode 100644
index 0000000000..766a716f9a
--- /dev/null
+++ b/source3/lib/asys/asys.c
@@ -0,0 +1,274 @@
+/*
+ * Async syscalls
+ * Copyright (C) Volker Lendecke 2012
+ *
+ * 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; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "asys.h"
+#include <stdlib.h>
+#include <errno.h>
+#include "../pthreadpool/pthreadpool.h"
+
+struct asys_pwrite_args {
+ int fildes;
+ const void *buf;
+ size_t nbyte;
+ off_t offset;
+};
+
+struct asys_pread_args {
+ int fildes;
+ void *buf;
+ size_t nbyte;
+ off_t offset;
+};
+
+union asys_job_args {
+ struct asys_pwrite_args pwrite_args;
+ struct asys_pread_args pread_args;
+};
+
+struct asys_job {
+ void *private_data;
+ union asys_job_args args;
+ ssize_t ret;
+ int err;
+ char busy;
+ char canceled;
+};
+
+struct asys_context {
+ struct pthreadpool *pool;
+ int pthreadpool_fd;
+
+ unsigned num_jobs;
+ struct asys_job **jobs;
+};
+
+struct asys_creds_context {
+ int dummy;
+};
+
+int asys_context_init(struct asys_context **pctx, unsigned max_parallel)
+{
+ struct asys_context *ctx;
+ int ret;
+
+ ctx = calloc(1, sizeof(struct asys_context));
+ if (ctx == NULL) {
+ return ENOMEM;
+ }
+ ret = pthreadpool_init(max_parallel, &ctx->pool);
+ if (ret != 0) {
+ free(ctx);
+ return ret;
+ }
+ ctx->pthreadpool_fd = pthreadpool_signal_fd(ctx->pool);
+
+ *pctx = ctx;
+ return 0;
+}
+
+int asys_signalfd(struct asys_context *ctx)
+{
+ return ctx->pthreadpool_fd;
+}
+
+int asys_context_destroy(struct asys_context *ctx)
+{
+ int ret;
+ unsigned i;
+
+ for (i=0; i<ctx->num_jobs; i++) {
+ if (ctx->jobs[i]->busy) {
+ return EBUSY;
+ }
+ }
+
+ ret = pthreadpool_destroy(ctx->pool);
+ if (ret != 0) {
+ return ret;
+ }
+ for (i=0; i<ctx->num_jobs; i++) {
+ free(ctx->jobs[i]);
+ }
+ free(ctx->jobs);
+ free(ctx);
+ return 0;
+}
+
+static int asys_new_job(struct asys_context *ctx, int *jobid,
+ struct asys_job **pjob)
+{
+ struct asys_job **tmp;
+ struct asys_job *job;
+ unsigned i;
+
+ for (i=0; i<ctx->num_jobs; i++) {
+ job = ctx->jobs[i];
+ if (!job->busy) {
+ job->err = 0;
+ *pjob = job;
+ *jobid = i;
+ return 0;
+ }
+ }
+
+ if (ctx->num_jobs+1 == 0) {
+ return EBUSY; /* overflow */
+ }
+
+ tmp = realloc(ctx->jobs, sizeof(struct asys_job *)*(ctx->num_jobs+1));
+ if (tmp == NULL) {
+ return ENOMEM;
+ }
+ ctx->jobs = tmp;
+
+ job = calloc(1, sizeof(struct asys_job));
+ if (job == NULL) {
+ return ENOMEM;
+ }
+ ctx->jobs[ctx->num_jobs] = job;
+
+ *jobid = ctx->num_jobs;
+ *pjob = job;
+ ctx->num_jobs += 1;
+ return 0;
+}
+
+static void asys_pwrite_do(void *private_data);
+
+int asys_pwrite(struct asys_context *ctx, int fildes, const void *buf,
+ size_t nbyte, off_t offset, void *private_data)
+{
+ struct asys_job *job;
+ struct asys_pwrite_args *args;
+ int jobid;
+ int ret;
+
+ ret = asys_new_job(ctx, &jobid, &job);
+ if (ret != 0) {
+ return ret;
+ }
+ job->private_data = private_data;
+
+ args = &job->args.pwrite_args;
+ args->fildes = fildes;
+ args->buf = buf;
+ args->nbyte = nbyte;
+ args->offset = offset;
+
+ ret = pthreadpool_add_job(ctx->pool, jobid, asys_pwrite_do, job);
+ if (ret != 0) {
+ return ret;
+ }
+ job->busy = 1;
+
+ return 0;
+}
+
+static void asys_pwrite_do(void *private_data)
+{
+ struct asys_job *job = (struct asys_job *)private_data;
+ struct asys_pwrite_args *args = &job->args.pwrite_args;
+
+ job->ret = pwrite(args->fildes, args->buf, args->nbyte, args->offset);
+ if (job->ret == -1) {
+ job->err = errno;
+ }
+}
+
+static void asys_pread_do(void *private_data);
+
+int asys_pread(struct asys_context *ctx, int fildes, void *buf,
+ size_t nbyte, off_t offset, void *private_data)
+{
+ struct asys_job *job;
+ struct asys_pread_args *args;
+ int jobid;
+ int ret;
+
+ ret = asys_new_job(ctx, &jobid, &job);
+ if (ret != 0) {
+ return ret;
+ }
+ job->private_data = private_data;
+
+ args = &job->args.pread_args;
+ args->fildes = fildes;
+ args->buf = buf;
+ args->nbyte = nbyte;
+ args->offset = offset;
+
+ ret = pthreadpool_add_job(ctx->pool, jobid, asys_pread_do, job);
+ if (ret != 0) {
+ return ret;
+ }
+ job->busy = 1;
+
+ return 0;
+}
+
+static void asys_pread_do(void *private_data)
+{
+ struct asys_job *job = (struct asys_job *)private_data;
+ struct asys_pread_args *args = &job->args.pread_args;
+
+ job->ret = pread(args->fildes, args->buf, args->nbyte, args->offset);
+ if (job->ret == -1) {
+ job->err = errno;
+ }
+}
+
+void asys_cancel(struct asys_context *ctx, void *private_data)
+{
+ unsigned i;
+
+ for (i=0; i<ctx->num_jobs; i++) {
+ struct asys_job *job = ctx->jobs[i];
+
+ if (job->private_data == private_data) {
+ job->canceled = 1;
+ }
+ }
+}
+
+int asys_result(struct asys_context *ctx, ssize_t *pret, int *perrno,
+ void *pdata)
+{
+ void **pprivate_data = (void **)pdata;
+ struct asys_job *job;
+ int ret, jobid;
+
+ ret = pthreadpool_finished_job(ctx->pool, &jobid);
+ if (ret != 0) {
+ return ret;
+ }
+ if ((jobid < 0) || (jobid >= ctx->num_jobs)) {
+ return EIO;
+ }
+
+ job = ctx->jobs[jobid];
+
+ if (job->canceled) {
+ return ECANCELED;
+ }
+
+ *pret = job->ret;
+ *perrno = job->err;
+ *pprivate_data = job->private_data;
+ job->busy = 0;
+ return 0;
+}
diff --git a/source3/lib/asys/asys.h b/source3/lib/asys/asys.h
new file mode 100644
index 0000000000..73f1051f61
--- /dev/null
+++ b/source3/lib/asys/asys.h
@@ -0,0 +1,146 @@
+/*
+ * Async syscalls
+ * Copyright (C) Volker Lendecke 2012
+ *
+ * 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; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __ASYS_H__
+#define __ASYS_H__
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+/**
+ * @defgroup asys The async syscall library
+ *
+ * This module contains a set of asynchronous functions that directly
+ * wrap normally synchronous posix system calls. The reason for this
+ * module's existence is the limited set of operations the posix async
+ * I/O API provides.
+ *
+ * The basic flow of operations is:
+ *
+ * The application creates a asys_context structure using
+ * asys_context_create()
+ *
+ * The application triggers a call to the library by calling for
+ * example asys_ftruncate(). asys_ftruncate() takes a private_data
+ * argument that will be returned later by asys_result. The calling
+ * application should hand a pointer representing the async operation
+ * to the private_data argument.
+ *
+ * The application puts the fd returned by asys_signalfd() into its
+ * event loop. When the signal fd becomes readable, the application
+ * calls asys_result() to grab the final result of one of the system
+ * calls that were issued in the meantime.
+ *
+ * For multi-user applications it is necessary to create different
+ * credential contexts, as it is not clear when exactly the real
+ * system call will be issued. The application might have called
+ * seteuid(2) or something equivalent in the meantime. Thus, all
+ * system calls doing access checks, in particular all calls doing
+ * path-based operations, require a struct auth_creds_context
+ * parameter. asys_creds_context_create() creates such a context. All
+ * credential-checking operations take a struct asys_creds_context as
+ * an argument. It can be NULL if the application never changes
+ * credentials.
+ *
+ * @{
+ */
+
+struct asys_context;
+struct asys_creds_context;
+
+enum asys_log_level {
+ ASYS_LOG_FATAL = 0,
+ ASYS_DEBUG_ERROR,
+ ASYS_DEBUG_WARNING,
+ ASYS_DEBUG_TRACE
+};
+
+#ifndef PRINTF_ATTRIBUTE
+#if (__GNUC__ >= 3)
+/** Use gcc attribute to check printf fns. a1 is the 1-based index of
+ * the parameter containing the format, and a2 the index of the first
+ * argument. Note that some gcc 2.x versions don't handle this
+ * properly **/
+#define PRINTF_ATTRIBUTE(a1, a2) __attribute__ ((format (__printf__, a1, a2)))
+#else
+#define PRINTF_ATTRIBUTE(a1, a2)
+#endif
+#endif
+
+typedef void (*asys_log_fn)(struct asys_context *ctx, void *private_data,
+ enum asys_log_level level,
+ const char *fmt, ...) PRINTF_ATTRIBUTE(4, 5);
+
+int asys_context_init(struct asys_context **ctx, unsigned max_parallel);
+int asys_context_destroy(struct asys_context *ctx);
+void asys_set_log_fn(struct asys_context *ctx, asys_log_fn fn,
+ void *private_data);
+
+/**
+ * @brief Get the the signal fd
+ *
+ * asys_signalfd() returns a file descriptor that will become readable
+ * whenever an asynchronous request has finished. When the signalfd is
+ * readable, calling asys_result() will not block.
+ *
+ * @param[in] ctx The asys context
+ * @return A file descriptor indicating a finished operation
+ */
+
+int asys_signalfd(struct asys_context *ctx);
+
+/**
+ * @brief Pull the result from an async operation
+ *
+ * Whe the fd returned from asys_signalfd() is readable, an async
+ * operation has finished. The result from the async operation can be
+ * pulled with asys_result().
+ *
+ * @param[in] ctx The asys context
+ * @return success: 0, failure: errno
+ */
+int asys_result(struct asys_context *ctx, ssize_t *pret, int *perrno,
+ void *pdata);
+void asys_cancel(struct asys_context *ctx, void *private_data);
+
+int asys_pread(struct asys_context *ctx, int fildes, void *buf, size_t nbyte,
+ off_t offset, void *private_data);
+int asys_pwrite(struct asys_context *ctx, int fildes, const void *buf,
+ size_t nbyte, off_t offset, void *private_data);
+int asys_ftruncate(struct asys_context *ctx, int filedes, off_t length,
+ void *private_data);
+int asys_fsync(struct asys_context *ctx, int fd, void *private_data);
+int asys_close(struct asys_context *ctx, int fd, void *private_data);
+
+struct asys_creds_context *asys_creds_context_create(
+ struct asys_context *ctx,
+ uid_t uid, gid_t gid, unsigned num_gids, gid_t *gids);
+
+int asys_creds_context_delete(struct asys_creds_context *ctx);
+
+int asys_open(struct asys_context *ctx, struct asys_creds_context *cctx,
+ const char *pathname, int flags, mode_t mode,
+ void *private_data);
+int asys_unlink(struct asys_context *ctx, struct asys_creds_context *cctx,
+ const char *pathname, void *private_data);
+
+/* @} */
+
+#endif /* __ASYS_H__ */
diff --git a/source3/lib/asys/tests.c b/source3/lib/asys/tests.c
new file mode 100644
index 0000000000..354f1bfbdb
--- /dev/null
+++ b/source3/lib/asys/tests.c
@@ -0,0 +1,92 @@
+/*
+ * Test async syscalls
+ * Copyright (C) Volker Lendecke 2012
+ *
+ * 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; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "asys.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+
+int main(int argc, const char *argv[])
+{
+ struct asys_context *ctx;
+ int i, fd, ret;
+
+ int *buf;
+
+ int ntasks = 10;
+
+ ret = asys_context_init(&ctx, 0);
+ if (ret != 0) {
+ perror("asys_context_create failed");
+ return 1;
+ }
+
+ fd = open("asys_testfile", O_CREAT|O_RDWR, 0644);
+ if (fd == -1) {
+ perror("open failed");
+ return 1;
+ }
+
+ buf = calloc(ntasks, sizeof(int));
+ if (buf == NULL) {
+ perror("calloc failed");
+ return 1;
+ }
+
+ for (i=0; i<ntasks; i++) {
+ buf[i] = i;
+ }
+
+ for (i=0; i<ntasks; i++) {
+ ret = asys_pwrite(ctx, fd, &buf[i], sizeof(int),
+ i * sizeof(int), &buf[i]);
+ if (ret != 0) {
+ errno = ret;
+ perror("asys_pwrite failed");
+ return 1;
+ }
+ }
+
+ for (i=0; i<ntasks; i++) {
+ void *priv;
+ ssize_t retval;
+ int err;
+ int *pidx;
+
+ ret = asys_result(ctx, &retval, &err, &priv);
+ if (ret == -1) {
+ errno = ret;
+ perror("asys_result failed");
+ return 1;
+ }
+ pidx = (int *)priv;
+
+ printf("%d returned %d\n", *pidx, (int)retval);
+ }
+
+ ret = asys_context_destroy(ctx);
+ if (ret != 0) {
+ perror("asys_context_delete failed");
+ return 1;
+ }
+
+ free(buf);
+
+ return 0;
+}
diff --git a/source3/lib/asys/wscript_build b/source3/lib/asys/wscript_build
new file mode 100644
index 0000000000..7b6d2365e6
--- /dev/null
+++ b/source3/lib/asys/wscript_build
@@ -0,0 +1,9 @@
+#!/usr/bin/env python
+
+bld.SAMBA3_SUBSYSTEM('LIBASYS',
+ source='asys.c',
+ deps='PTHREADPOOL')
+
+bld.SAMBA3_BINARY('asystest',
+ source='tests.c',
+ deps='LIBASYS')
diff --git a/source3/wscript_build b/source3/wscript_build
index 0324f47df3..672129dbcc 100755
--- a/source3/wscript_build
+++ b/source3/wscript_build
@@ -1602,6 +1602,7 @@ bld.INSTALL_FILES('${SWATDIR}', swat_files, base_name='../swat')
bld.RECURSE('auth')
bld.RECURSE('libgpo/gpext')
bld.RECURSE('lib/pthreadpool')
+bld.RECURSE('lib/asys')
bld.RECURSE('librpc')
bld.RECURSE('librpc/idl')
bld.RECURSE('libsmb')