blob: 8043268d5b2d9e7d41473b128e4b7fc0a14fae3f [file]
#include <stdlib.h>
#include <unistd.h>
#include "config.h"
#include "workqueue.h"
#include "xlog.h"
#if defined(HAVE_SCHED_H) && defined(HAVE_LIBPTHREAD) && defined(HAVE_UNSHARE)
#include <sched.h>
#include <pthread.h>
struct xwork_struct {
struct xwork_struct *next;
void (*fn)(void *);
void *data;
};
struct xwork_queue {
struct xwork_struct *head;
struct xwork_struct **tail;
unsigned char shutdown : 1;
};
static void xwork_queue_init(struct xwork_queue *queue)
{
queue->head = NULL;
queue->tail = &queue->head;
queue->shutdown = 0;
}
static void xwork_enqueue(struct xwork_queue *queue,
struct xwork_struct *entry)
{
entry->next = NULL;
*queue->tail = entry;
queue->tail = &entry->next;
}
static struct xwork_struct *xwork_dequeue(struct xwork_queue *queue)
{
struct xwork_struct *entry = NULL;
if (queue->head) {
entry = queue->head;
queue->head = entry->next;
if (!queue->head)
queue->tail = &queue->head;
}
return entry;
}
struct xthread_work {
struct xwork_struct work;
pthread_cond_t cond;
};
struct xthread_workqueue {
struct xwork_queue queue;
pthread_mutex_t mutex;
pthread_cond_t cond;
};
static void xthread_workqueue_init(struct xthread_workqueue *wq)
{
xwork_queue_init(&wq->queue);
pthread_mutex_init(&wq->mutex, NULL);
pthread_cond_init(&wq->cond, NULL);
}
static void xthread_workqueue_fini(struct xthread_workqueue *wq)
{
pthread_cond_destroy(&wq->cond);
pthread_mutex_destroy(&wq->mutex);
}
static int xthread_work_enqueue(struct xthread_workqueue *wq,
struct xthread_work *work)
{
xwork_enqueue(&wq->queue, &work->work);
pthread_cond_signal(&wq->cond);
return 0;
}
static struct xthread_work *xthread_work_dequeue(struct xthread_workqueue *wq)
{
return (struct xthread_work *)xwork_dequeue(&wq->queue);
}
static void xthread_workqueue_do_work(struct xthread_workqueue *wq)
{
struct xthread_work *work;
pthread_mutex_lock(&wq->mutex);
/* Signal the caller that we're up and running */
pthread_cond_signal(&wq->cond);
for (;;) {
work = xthread_work_dequeue(wq);
if (work) {
work->work.fn(work->work.data);
pthread_cond_signal(&work->cond);
continue;
}
if (wq->queue.shutdown)
break;
pthread_cond_wait(&wq->cond, &wq->mutex);
}
pthread_mutex_unlock(&wq->mutex);
}
void xthread_workqueue_shutdown(struct xthread_workqueue *wq)
{
pthread_mutex_lock(&wq->mutex);
wq->queue.shutdown = 1;
pthread_cond_signal(&wq->cond);
pthread_mutex_unlock(&wq->mutex);
}
static void xthread_workqueue_free(struct xthread_workqueue *wq)
{
xthread_workqueue_fini(wq);
free(wq);
}
static void xthread_workqueue_cleanup(void *data)
{
xthread_workqueue_free(data);
}
static void *xthread_workqueue_worker(void *data)
{
pthread_cleanup_push(xthread_workqueue_cleanup, data);
xthread_workqueue_do_work(data);
pthread_cleanup_pop(1);
return NULL;
}
struct xthread_workqueue *xthread_workqueue_alloc(void)
{
struct xthread_workqueue *ret;
pthread_t thread;
ret = malloc(sizeof(*ret));
if (ret) {
xthread_workqueue_init(ret);
pthread_mutex_lock(&ret->mutex);
if (pthread_create(&thread, NULL,
xthread_workqueue_worker,
ret) == 0) {
/* Wait for thread to start */
pthread_cond_wait(&ret->cond, &ret->mutex);
pthread_mutex_unlock(&ret->mutex);
return ret;
}
pthread_mutex_unlock(&ret->mutex);
xthread_workqueue_free(ret);
ret = NULL;
}
return NULL;
}
void xthread_work_run_sync(struct xthread_workqueue *wq,
void (*fn)(void *), void *data)
{
struct xthread_work work = {
{
NULL,
fn,
data
},
PTHREAD_COND_INITIALIZER,
};
pthread_mutex_lock(&wq->mutex);
xthread_work_enqueue(wq, &work);
pthread_cond_wait(&work.cond, &wq->mutex);
pthread_mutex_unlock(&wq->mutex);
pthread_cond_destroy(&work.cond);
}
static void xthread_workqueue_do_chroot(void *data)
{
const char *path = data;
if (unshare(CLONE_FS) != 0) {
xlog_err("unshare() failed: %m");
return;
}
if (chroot(path) != 0)
xlog_err("chroot(%s) failed: %m", path);
}
void xthread_workqueue_chroot(struct xthread_workqueue *wq,
const char *path)
{
xthread_work_run_sync(wq, xthread_workqueue_do_chroot, (void *)path);
}
#else
struct xthread_workqueue {
};
static struct xthread_workqueue ret;
struct xthread_workqueue *xthread_workqueue_alloc(void)
{
return &ret;
}
void xthread_workqueue_shutdown(struct xthread_workqueue *wq)
{
}
void xthread_work_run_sync(struct xthread_workqueue *wq,
void (*fn)(void *), void *data)
{
fn(data);
}
void xthread_workqueue_chroot(struct xthread_workqueue *wq,
const char *path)
{
xlog_err("Unable to run as chroot");
}
#endif /* defined(HAVE_SCHED_H) && defined(HAVE_LIBPTHREAD) && defined(HAVE_UNSHARE) */