wmii/libixp/server.c

439 lines
9.1 KiB
C
Raw Normal View History

2005-11-18 18:54:58 +03:00
/*
* (C)opyright MMIV-MMV Anselm R. Garbe <garbeam at gmail dot com>
* See LICENSE file for license details.
*/
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <sys/un.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include "ixp.h"
#include <cext.h>
2005-12-05 01:45:59 +03:00
static Connection zero_conn = { 0 };
static int user_fd = -1;
2005-11-18 18:54:58 +03:00
2005-12-05 01:45:59 +03:00
void set_error(IXPServer * s, char *errstr)
2005-11-18 18:54:58 +03:00
{
if (s->errstr)
free(s->errstr);
if (errstr)
s->errstr = strdup(errstr);
else
s->errstr = 0;
}
2005-12-05 01:45:59 +03:00
File *fd_to_file(IXPServer * s, int fd)
2005-11-18 18:54:58 +03:00
{
2005-12-05 01:45:59 +03:00
int cidx = fd / MAX_CONN;
int fidx = fd - (cidx * MAX_CONN);
2005-11-18 18:54:58 +03:00
return s->conn[cidx].files[fidx];
}
2005-12-05 01:45:59 +03:00
static void handle_ixp_create(Connection * c)
2005-11-18 18:54:58 +03:00
{
c->s->create(c->s, ((char *) c->data) + sizeof(ReqHeader));
free(c->data);
c->data = c->s->errstr ?
rerror_message(c->s->errstr, &c->len) : rcreate_message(&c->len);
c->remain = c->len;
}
2005-12-05 01:45:59 +03:00
static void handle_ixp_open(Connection * c)
2005-11-18 18:54:58 +03:00
{
2005-12-05 01:45:59 +03:00
int i;
2005-11-18 18:54:58 +03:00
/* seek next free slot */
for (i = 0; (i < MAX_OPEN_FILES) && c->files[i]; i++);
if (i == MAX_OPEN_FILES) {
fprintf(stderr, "%s",
2005-12-05 01:45:59 +03:00
"ixp: server: maximum of open files, try again later.\n");
2005-11-18 18:54:58 +03:00
free(c->data);
c->data =
rerror_message("maximum open files reached, close files first",
2005-12-05 01:45:59 +03:00
&c->len);
2005-11-18 18:54:58 +03:00
c->remain = c->len;
return;
}
c->files[i] = c->s->open(c->s, ((char *) c->data) + sizeof(ReqHeader));
c->seen[i] = MAX_SEEN_SHUTDOWN;
free(c->data);
c->data = c->s->errstr ?
rerror_message(c->s->errstr,
2005-12-05 01:45:59 +03:00
&c->len) : ropen_message(i + MAX_CONN * c->index,
&c->len);
2005-11-18 18:54:58 +03:00
c->remain = c->len;
}
2005-12-05 01:45:59 +03:00
static void handle_ixp_read(Connection * c, ReqHeader * h)
2005-11-18 18:54:58 +03:00
{
2005-12-05 01:45:59 +03:00
void *data = 0;
size_t out_len;
2005-11-18 18:54:58 +03:00
data = cext_emallocz(h->buf_len);
2005-11-18 18:54:58 +03:00
out_len = c->s->read(c->s, h->fd, h->offset, data, h->buf_len);
free(c->data);
if (c->s->errstr)
2005-11-18 18:54:58 +03:00
c->data = rerror_message(c->s->errstr, &c->len);
else
2005-11-18 18:54:58 +03:00
c->data = rread_message(data, out_len, &c->len);
c->remain = c->len;
free(data);
}
2005-12-05 01:45:59 +03:00
static void handle_ixp_write(Connection * c, ReqHeader * h)
2005-11-18 18:54:58 +03:00
{
c->s->write(c->s, h->fd, h->offset,
2005-12-05 01:45:59 +03:00
((char *) c->data) + sizeof(ReqHeader), h->buf_len);
2005-11-18 18:54:58 +03:00
free(c->data);
if (c->s->errstr)
2005-11-18 18:54:58 +03:00
c->data = rerror_message(c->s->errstr, &c->len);
else
2005-11-18 18:54:58 +03:00
c->data = rwrite_message(&c->len);
c->remain = c->len;
}
2005-12-05 01:45:59 +03:00
static void handle_ixp_close(Connection * c, ReqHeader * h)
2005-11-18 18:54:58 +03:00
{
2005-12-05 01:45:59 +03:00
int fidx = h->fd - (c->index * MAX_CONN);
2005-11-18 18:54:58 +03:00
c->s->close(c->s, h->fd);
c->files[fidx] = 0;
free(c->data);
if (c->s->errstr)
2005-11-18 18:54:58 +03:00
c->data = rerror_message(c->s->errstr, &c->len);
else
2005-11-18 18:54:58 +03:00
c->data = rclose_message(&c->len);
c->remain = c->len;
}
2005-12-05 01:45:59 +03:00
static void handle_ixp_remove(Connection * c)
2005-11-18 18:54:58 +03:00
{
c->s->remove(c->s, ((char *) c->data) + sizeof(ReqHeader));
free(c->data);
c->data = c->s->errstr ?
rerror_message(c->s->errstr, &c->len) : rremove_message(&c->len);
c->remain = c->len;
}
2005-12-05 01:45:59 +03:00
static void check_ixp_request(Connection * c)
2005-11-18 18:54:58 +03:00
{
2005-12-05 01:45:59 +03:00
ReqHeader h;
2005-11-18 18:54:58 +03:00
/* check pending request */
if (c->s->errstr)
set_error(c->s, 0);
memcpy(&h, c->data, sizeof(ReqHeader));
switch (h.req) {
case TCREATE:
handle_ixp_create(c);
break;
case TREMOVE:
handle_ixp_remove(c);
break;
case TOPEN:
handle_ixp_open(c);
break;
case TCLUNK:
handle_ixp_close(c, &h);
break;
case TREAD:
handle_ixp_read(c, &h);
break;
case TWRITE:
handle_ixp_write(c, &h);
break;
default:
fprintf(stderr, "%s", "ixp: server: invalid request\n");
free(c->data);
c->len = c->remain = 0;
break;
}
}
2005-12-05 01:45:59 +03:00
static void update_conns(IXPServer * s)
2005-11-18 18:54:58 +03:00
{
2005-12-05 01:45:59 +03:00
int i;
2005-11-18 18:54:58 +03:00
FD_ZERO(&s->rd);
FD_ZERO(&s->wr);
for (i = 0; i < MAX_CONN; i++) {
if (s->conn[i].fd >= 0) {
s->nfds = _MAX(s->nfds, s->conn[i].fd);
if (s->conn[i].read && !s->conn[i].mode
2005-12-05 01:45:59 +03:00
&& (!s->conn[i].len || s->conn[i].remain)) {
2005-11-18 18:54:58 +03:00
FD_SET(s->conn[i].fd, &s->rd);
}
if (s->conn[i].write && s->conn[i].mode && s->conn[i].remain) {
FD_SET(s->conn[i].fd, &s->wr);
}
}
}
}
2005-12-05 01:45:59 +03:00
static void close_conn(Connection * c)
2005-11-18 18:54:58 +03:00
{
2005-12-05 01:45:59 +03:00
int i;
2005-11-18 18:54:58 +03:00
/* shutdown connection and cleanup open files */
shutdown(c->fd, SHUT_RDWR);
close(c->fd);
c->fd = -1;
c->mode = 0;
for (i = 0; i < MAX_OPEN_FILES; i++) {
if (c->files[i])
2005-11-18 18:54:58 +03:00
c->files[i] = 0;
}
}
2005-12-05 01:45:59 +03:00
static void read_conn(Connection * c)
2005-11-18 18:54:58 +03:00
{
2005-12-05 01:45:59 +03:00
size_t r;
2005-11-18 18:54:58 +03:00
if (!c->header) {
r = read(c->fd, &c->len, sizeof(size_t));
if (r != sizeof(size_t)) {
close_conn(c);
return;
}
c->remain = c->len;
c->data = cext_emallocz(c->len);
2005-11-18 18:54:58 +03:00
c->header = 1;
}
r = read(c->fd, ((char *) c->data) + c->len - c->remain, c->remain);
if (r < 1) {
close_conn(c);
return;
}
c->remain -= r;
if (c->remain == 0) {
/* check IXP request */
2005-12-05 01:45:59 +03:00
c->mode = 1; /* next mode is response */
2005-11-18 18:54:58 +03:00
check_ixp_request(c);
c->header = 0;
}
}
2005-12-05 01:45:59 +03:00
static void write_conn(Connection * c)
2005-11-18 18:54:58 +03:00
{
2005-12-05 01:45:59 +03:00
size_t r;
2005-11-18 18:54:58 +03:00
if (!c->header) {
r = write(c->fd, &c->len, sizeof(size_t));
if (r != sizeof(size_t)) {
close_conn(c);
}
c->header = 1;
}
r = write(c->fd, ((char *) c->data) + c->len - c->remain, c->remain);
if (r < 1) {
close_conn(c);
return;
}
c->remain -= r;
if (c->remain == 0) {
c->len = 0;
c->mode = 0;
c->header = 0;
}
}
2005-12-05 01:45:59 +03:00
static void new_conn(Connection * c)
2005-11-18 18:54:58 +03:00
{
2005-12-05 01:45:59 +03:00
int r, i;
socklen_t l;
struct sockaddr_un name = { 0 };
2005-11-18 18:54:58 +03:00
l = sizeof(name);
2005-12-05 01:45:59 +03:00
if ((r = accept(c->fd, (struct sockaddr *) &name, &l)) < 0) {
2005-11-18 18:54:58 +03:00
perror("ixp: server: cannot accept connection");
return;
}
if (c->s->runlevel == SHUTDOWN) {
fprintf(stderr, "%s",
2005-12-05 01:45:59 +03:00
"ixp: server: connection refused, server is shutting down.\n");
2005-11-18 18:54:58 +03:00
close(r);
return;
}
for (i = 0; i < MAX_CONN; i++) {
if (c->s->conn[i].fd == -1) { /* free connection */
c->s->conn[i] = zero_conn;
c->s->conn[i].s = c->s;
c->s->conn[i].index = i;
c->s->conn[i].fd = r;
c->s->conn[i].read = read_conn;
c->s->conn[i].write = write_conn;
break;
}
}
if (i == MAX_CONN) {
fprintf(stderr, "%s",
2005-12-05 01:45:59 +03:00
"ixp: server: connection refused, try again later.\n");
2005-11-18 18:54:58 +03:00
close(r);
}
}
2005-12-05 01:45:59 +03:00
static int check_open_files(Connection * c)
2005-11-18 18:54:58 +03:00
{
2005-12-05 01:45:59 +03:00
int i;
2005-11-18 18:54:58 +03:00
for (i = 0; i < MAX_OPEN_FILES; i++) {
if (c->files[i] && c->seen[i]) {
c->seen[i]--;
return 1;
}
}
return 0;
}
2005-12-05 01:45:59 +03:00
static void handle_socks(IXPServer * s)
2005-11-18 18:54:58 +03:00
{
2005-12-05 01:45:59 +03:00
int i, now = 1;
2005-11-18 18:54:58 +03:00
for (i = 0; i < MAX_CONN; i++) {
if (s->conn[i].fd >= 0) {
if (FD_ISSET(s->conn[i].fd, &s->rd) && s->conn[i].read) {
/* call back read handler */
s->conn[i].read(&s->conn[i]);
} else if (FD_ISSET(s->conn[i].fd, &s->wr) && s->conn[i].write) {
/* call back write handler */
s->conn[i].write(&s->conn[i]);
}
/*
* don't shutdown, if there're remaining bits or if
* still responses are sent or still opened files
*/
if ((s->runlevel == SHUTDOWN)
2005-12-05 01:45:59 +03:00
&& (check_open_files(&s->conn[i])
|| (s->conn[i].remain > 0)
|| s->conn[i].mode))
2005-11-18 18:54:58 +03:00
now = 0;
}
}
if ((s->runlevel == SHUTDOWN) && now)
2005-12-05 01:45:59 +03:00
s->runlevel = HALT; /* real stop */
2005-11-18 18:54:58 +03:00
}
2005-12-05 01:45:59 +03:00
IXPServer *init_server(char *sockfile, void (*cleanup) (void))
2005-11-18 18:54:58 +03:00
{
2005-12-05 01:45:59 +03:00
int i;
struct sockaddr_un addr = { 0 };
int yes = 1;
socklen_t su_len;
IXPServer *s;
2005-11-18 18:54:58 +03:00
/* init */
s = (IXPServer *) cext_emallocz(sizeof(IXPServer));
2005-11-18 18:54:58 +03:00
s->sockfile = sockfile;
s->root = (File *) cext_emallocz(sizeof(File));
2005-12-05 01:45:59 +03:00
s->runlevel = HALT; /* initially server is not running */
2005-11-18 18:54:58 +03:00
s->create = ixp_create;
s->remove = ixp_remove;
s->open = ixp_open;
s->close = ixp_close;
s->read = ixp_read;
s->write = ixp_write;
s->root->name = strdup("");
for (i = 0; i < MAX_CONN; i++) {
s->conn[i].s = s;
s->conn[i].fd = -1;
s->conn[i].index = i;
}
signal(SIGPIPE, SIG_IGN);
if ((s->conn[0].fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
perror("ixp: server: socket");
free(s);
return 0;
}
if (setsockopt(s->conn[0].fd, SOL_SOCKET, SO_REUSEADDR,
2005-12-05 01:45:59 +03:00
(char *) &yes, sizeof(yes)) < 0) {
2005-11-18 18:54:58 +03:00
perror("ixp: server: setsockopt");
close(s->conn[0].fd);
free(s);
return 0;
}
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, sockfile, sizeof(addr.sun_path));
su_len = sizeof(struct sockaddr) + strlen(addr.sun_path);
2005-12-05 01:45:59 +03:00
if (bind(s->conn[0].fd, (struct sockaddr *) &addr, su_len) < 0) {
2005-11-18 18:54:58 +03:00
perror("ixp: server: cannot bind socket");
close(s->conn[0].fd);
free(s);
return 0;
}
chmod(sockfile, S_IRWXU);
if (listen(s->conn[0].fd, MAX_CONN) < 0) {
perror("ixp: server: cannot listen on socket");
close(s->conn[0].fd);
free(s);
return 0;
}
s->conn[0].read = new_conn;
/* register to cleanup function, to unlink sockfile */
if (cleanup)
atexit(cleanup);
return s;
}
void
run_server_with_fd_support(IXPServer * s, int fd,
2005-12-05 01:45:59 +03:00
void (*fd_read) (Connection *),
void (*fd_write) (Connection *))
2005-11-18 18:54:58 +03:00
{
s->conn[1] = zero_conn;
s->conn[1].index = 1;
s->conn[1].s = s;
s->conn[1].fd = user_fd = fd;
s->conn[1].read = fd_read;
s->conn[1].write = fd_write;
run_server(s);
}
2005-12-05 01:45:59 +03:00
void run_server(IXPServer * s)
2005-11-18 18:54:58 +03:00
{
2005-12-05 01:45:59 +03:00
int r, i;
2005-11-18 18:54:58 +03:00
s->runlevel = RUNNING;
/* main loop */
while (s->runlevel != HALT) {
update_conns(s);
r = select(s->nfds + 1, &s->rd, &s->wr, 0, 0);
if (r == -1 && errno == EINTR)
continue;
if (r < 0) {
perror("ixp: server: select");
2005-12-05 01:45:59 +03:00
break; /* allow cleanups in IXP using app */
2005-11-18 18:54:58 +03:00
} else if (r > 0) {
handle_socks(s);
}
}
/* shut down server */
for (i = MAX_CONN - 1; i >= 0; i--) {
if (s->conn[i].fd >= 0 && s->conn[i].fd != user_fd) {
close(s->conn[i].fd);
}
}
}
2005-12-05 01:45:59 +03:00
void deinit_server(IXPServer * s)
2005-11-18 18:54:58 +03:00
{
unlink(s->sockfile);
ixp_remove(s, "/");
free(s);
}