wmii/cmd/wmiifs.c

409 lines
8.8 KiB
C

/*
* (C)opyright MMVI Anselm R. Garbe <garbeam at gmail dot com>
* See LICENSE file for license details.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "wmii.h"
/* array indexes for file pointers */
typedef enum {
F_CTL,
F_LAST
} FsIndexes;
typedef struct Route Route;
struct Route {
File dest;
int src;
};
typedef struct Bind Bind;
struct Bind {
IXPClient *client;
Route route[MAX_CONN * MAX_OPEN_FILES];
File *mount;
char *prefix;
Bind *next;
};
static Display *dpy;
static IXPServer *ixps;
static char *sockfile = nil;
static File *files[F_LAST];
static Bind *bindings = nil;
static void quit(void *obj, char *arg);
static void bind(void *obj, char *arg);
static void unbind(void *obj, char *arg);
static Action acttbl[] = {
{"quit", quit},
{"unbind", unbind},
{"bind", bind},
{0, 0}
};
static char version[] = "wmiifs - " VERSION ", (C)opyright MMIV-MMVI Anselm R. Garbe\n";
static void
usage()
{
fprintf(stderr, "usage: wmiifs -a <address> [-v]\n");
exit(1);
}
static void
quit(void *obj, char *arg)
{
Bind *b;
while((b = bindings)) {
bindings = bindings->next;
if(b->mount) {
b->mount->content = nil;
ixp_remove(ixps, b->prefix);
if(ixps->errstr)
fprintf(stderr, "wmiifs: error on unbind %s: %s\n",
b->prefix, ixps->errstr);
}
free(b->prefix);
free(b);
}
bindings = nil;
ixps->runlevel = SHUTDOWN;
}
static void
do_unbind(Bind * bind)
{
Bind *b;
if(bind == bindings)
bindings = bind->next;
else {
for(b = bindings; b && b->next != bind; b = b->next);
b->next = bind->next;
}
/* free stuff */
deinit_client(bind->client);
free(bind->prefix);
free(bind);
}
static void
unbind(void *obj, char *arg)
{
Bind *b;
for(b = bindings; b && strncmp(b->prefix, arg, strlen(b->prefix));
b = b->next);
if(!b) {
fprintf(stderr, "wmiifs: unbind: '%s' no such path\n", arg);
return;
}
do_unbind(b);
}
static void
bind(void *obj, char *arg)
{
Bind *b, *new = nil;
char cmd[1024];
char *sfile;
if(!arg)
return;
cext_strlcpy(cmd, arg, sizeof(cmd));
sfile = strchr(cmd, ' ');
if(!sfile) {
fprintf(stderr,
"wmiifs: bind: '%s' without socket argument, ignoring\n",
arg);
return; /* shortcut with empty argument */
}
*sfile = 0;
sfile++;
if(*sfile == 0) {
fprintf(stderr,
"wmiifs: bind: '%s' without socket argument, ignoring\n",
arg);
return; /* shortcut with empty argument */
}
new = cext_emallocz(sizeof(Bind));
new->client = init_ixp_client(sfile);
if(!new->client) {
fprintf(stderr,
"wmiifs: bind: cannot connect to server '%s', ignoring\n",
sfile);
free(new);
return;
}
new->prefix = strdup(cmd);
new->mount = ixp_create(ixps, new->prefix);
new->mount->content = new->mount; /* shall be a directory */
if(!bindings)
bindings = new;
else {
for(b = bindings; b && b->next; b = b->next);
b->next = new;
}
}
static void
handle_after_write(IXPServer * s, File * f)
{
int i;
size_t len;
for(i = 0; acttbl[i].name; i++) {
len = strlen(acttbl[i].name);
if(!strncmp(acttbl[i].name, (char *) f->content, len)) {
if(strlen(f->content) > len) {
acttbl[i].func(0, &((char *) f->content)[len + 1]);
} else {
acttbl[i].func(0, 0);
}
break;
}
}
}
static Bind *
fd_to_bind(int fd, int *client_fd)
{
File *f = fd_to_file(ixps, fd);
unsigned int i;
Bind *b;
if(!f)
return nil;
for(b = bindings; b; b = b->next) {
for(i = 0; i < MAX_CONN * MAX_OPEN_FILES; i++) {
if(&b->route[i].dest == f) {
*client_fd = b->route[i].src;
return b;
}
}
}
return nil;
}
static File *
fixp_create(IXPServer * s, char *path)
{
Bind *b;
size_t len;
for(b = bindings; b && strncmp(b->prefix, path, strlen(b->prefix));
b = b->next);
if(!b) {
File *f = ixp_create(s, path);
return f;
}
/* route to b */
len = strlen(b->prefix);
b->client->create(b->client, path[len] == 0 ? "/" : &path[len]);
if(b->client->errstr) {
if(!strcmp(b->client->errstr, DEAD_SERVER))
do_unbind(b);
}
return nil;
}
static File *
fixp_open(IXPServer * s, char *path)
{
Bind *b;
int fd;
size_t len;
for(b = bindings; b && strncmp(b->prefix, path, strlen(b->prefix));
b = b->next);
if(!b) {
File *f = ixp_open(s, path);
return f;
}
/* route to b */
len = strlen(b->prefix);
fd = b->client->open(b->client, path[len] == 0 ? "/" : &path[len]);
if(b->client->errstr) {
set_error(s, b->client->errstr);
if(!strcmp(b->client->errstr, DEAD_SERVER))
do_unbind(b);
return nil;
}
b->route[fd].src = fd;
return &b->route[fd].dest;
}
static size_t
fixp_read(IXPServer * s, int fd, size_t offset, void *out_buf,
size_t out_buf_len)
{
int cfd;
Bind *b = fd_to_bind(fd, &cfd);
size_t result;
if(!b) {
result = ixp_read(s, fd, offset, out_buf, out_buf_len);
return result;
}
/* route to b */
result = seek_read(b->client, cfd, offset, out_buf, out_buf_len);
if(b->client->errstr) {
set_error(s, b->client->errstr);
if(!strcmp(b->client->errstr, DEAD_SERVER))
do_unbind(b);
}
return result;
}
static void
fixp_write(IXPServer * s, int fd, size_t offset, void *content,
size_t in_len)
{
int cfd;
Bind *b = fd_to_bind(fd, &cfd);
if(!b) {
ixp_write(s, fd, offset, content, in_len);
return;
}
/* route to b */
seek_write(b->client, cfd, offset, content, in_len);
if(b->client->errstr) {
set_error(s, b->client->errstr);
if(!strcmp(b->client->errstr, DEAD_SERVER))
do_unbind(b);
}
}
static void
fixp_close(IXPServer * s, int fd)
{
int cfd;
Bind *b = fd_to_bind(fd, &cfd);
if(!b) {
ixp_close(s, fd);
return;
}
/* route to b */
b->client->close(b->client, cfd);
if(b->client->errstr) {
set_error(s, b->client->errstr);
if(!strcmp(b->client->errstr, DEAD_SERVER))
do_unbind(b);
}
}
static void
fixp_remove(IXPServer * s, char *path)
{
Bind *b;
size_t len;
for(b = bindings; b && strncmp(b->prefix, path, strlen(b->prefix));
b = b->next);
if(!b) {
ixp_remove(s, path);
return;
}
/* route to b */
len = strlen(b->prefix);
b->client->remove(b->client, path[len] == 0 ? "/" : &path[len]);
if(b->client->errstr) {
set_error(s, b->client->errstr);
if(!strcmp(b->client->errstr, DEAD_SERVER))
do_unbind(b);
}
}
static void
check_event(Connection * e)
{
XEvent ev;
while(XPending(dpy)) {
/*
* wmiifs isn't interested in any X events, so just drop them
* all
*/
XNextEvent(dpy, &ev);
}
/* why check them? because X won't kill wmiifs when X dies */
}
static void
run()
{
if(!(files[F_CTL] = ixp_create(ixps, "/ctl"))) {
perror("wmiifs: cannot connect IXP server");
exit(1);
}
files[F_CTL]->after_write = handle_after_write;
/* routing functions */
ixps->create = fixp_create;
ixps->remove = fixp_remove;
ixps->open = fixp_open;
ixps->close = fixp_close;
ixps->read = fixp_read;
ixps->write = fixp_write;
/* main event loop */
run_server_with_fd_support(ixps, ConnectionNumber(dpy),
check_event, 0);
deinit_server(ixps);
}
int
main(int argc, char *argv[])
{
int i;
/* command line args */
for(i = 1; (i < argc) && (argv[i][0] == '-'); i++) {
switch (argv[i][1]) {
case 'v':
fprintf(stdout, "%s", version[0]);
exit(0);
break;
case 's':
if(i + 1 < argc)
sockfile = argv[++i];
else
usage();
break;
default:
usage();
break;
}
}
if(!getenv("HOME")) {
fprintf(stderr, "%s",
"wmiifs: $HOME environment variable is not set\n");
usage();
}
/* just for the case X crashes/gets quit */
dpy = XOpenDisplay(0);
if(!dpy) {
fprintf(stderr, "%s", "wmiifs: cannot open display\n");
exit(1);
}
ixps = wmii_setup_server(sockfile);
run();
return 0;
}