diff options
Diffstat (limited to '')
-rw-r--r-- | arch/um/drivers/daemon_user.c | 193 |
1 files changed, 193 insertions, 0 deletions
diff --git a/arch/um/drivers/daemon_user.c b/arch/um/drivers/daemon_user.c new file mode 100644 index 000000000..8813c10d0 --- /dev/null +++ b/arch/um/drivers/daemon_user.c @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com) + * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org) and + * James Leu (jleu@mindspring.net). + * Copyright (C) 2001 by various other people who didn't put their name here. + * Licensed under the GPL. + */ + +#include <stdint.h> +#include <unistd.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <sys/un.h> +#include "daemon.h" +#include <net_user.h> +#include <os.h> +#include <um_malloc.h> + +enum request_type { REQ_NEW_CONTROL }; + +#define SWITCH_MAGIC 0xfeedface + +struct request_v3 { + uint32_t magic; + uint32_t version; + enum request_type type; + struct sockaddr_un sock; +}; + +static struct sockaddr_un *new_addr(void *name, int len) +{ + struct sockaddr_un *sun; + + sun = uml_kmalloc(sizeof(struct sockaddr_un), UM_GFP_KERNEL); + if (sun == NULL) { + printk(UM_KERN_ERR "new_addr: allocation of sockaddr_un " + "failed\n"); + return NULL; + } + sun->sun_family = AF_UNIX; + memcpy(sun->sun_path, name, len); + return sun; +} + +static int connect_to_switch(struct daemon_data *pri) +{ + struct sockaddr_un *ctl_addr = pri->ctl_addr; + struct sockaddr_un *local_addr = pri->local_addr; + struct sockaddr_un *sun; + struct request_v3 req; + int fd, n, err; + + pri->control = socket(AF_UNIX, SOCK_STREAM, 0); + if (pri->control < 0) { + err = -errno; + printk(UM_KERN_ERR "daemon_open : control socket failed, " + "errno = %d\n", -err); + return err; + } + + if (connect(pri->control, (struct sockaddr *) ctl_addr, + sizeof(*ctl_addr)) < 0) { + err = -errno; + printk(UM_KERN_ERR "daemon_open : control connect failed, " + "errno = %d\n", -err); + goto out; + } + + fd = socket(AF_UNIX, SOCK_DGRAM, 0); + if (fd < 0) { + err = -errno; + printk(UM_KERN_ERR "daemon_open : data socket failed, " + "errno = %d\n", -err); + goto out; + } + if (bind(fd, (struct sockaddr *) local_addr, sizeof(*local_addr)) < 0) { + err = -errno; + printk(UM_KERN_ERR "daemon_open : data bind failed, " + "errno = %d\n", -err); + goto out_close; + } + + sun = uml_kmalloc(sizeof(struct sockaddr_un), UM_GFP_KERNEL); + if (sun == NULL) { + printk(UM_KERN_ERR "new_addr: allocation of sockaddr_un " + "failed\n"); + err = -ENOMEM; + goto out_close; + } + + req.magic = SWITCH_MAGIC; + req.version = SWITCH_VERSION; + req.type = REQ_NEW_CONTROL; + req.sock = *local_addr; + n = write(pri->control, &req, sizeof(req)); + if (n != sizeof(req)) { + printk(UM_KERN_ERR "daemon_open : control setup request " + "failed, err = %d\n", -errno); + err = -ENOTCONN; + goto out_free; + } + + n = read(pri->control, sun, sizeof(*sun)); + if (n != sizeof(*sun)) { + printk(UM_KERN_ERR "daemon_open : read of data socket failed, " + "err = %d\n", -errno); + err = -ENOTCONN; + goto out_free; + } + + pri->data_addr = sun; + return fd; + + out_free: + kfree(sun); + out_close: + close(fd); + out: + close(pri->control); + return err; +} + +static int daemon_user_init(void *data, void *dev) +{ + struct daemon_data *pri = data; + struct timeval tv; + struct { + char zero; + int pid; + int usecs; + } name; + + if (!strcmp(pri->sock_type, "unix")) + pri->ctl_addr = new_addr(pri->ctl_sock, + strlen(pri->ctl_sock) + 1); + name.zero = 0; + name.pid = os_getpid(); + gettimeofday(&tv, NULL); + name.usecs = tv.tv_usec; + pri->local_addr = new_addr(&name, sizeof(name)); + pri->dev = dev; + pri->fd = connect_to_switch(pri); + if (pri->fd < 0) { + kfree(pri->local_addr); + pri->local_addr = NULL; + return pri->fd; + } + + return 0; +} + +static int daemon_open(void *data) +{ + struct daemon_data *pri = data; + return pri->fd; +} + +static void daemon_remove(void *data) +{ + struct daemon_data *pri = data; + + close(pri->fd); + pri->fd = -1; + close(pri->control); + pri->control = -1; + + kfree(pri->data_addr); + pri->data_addr = NULL; + kfree(pri->ctl_addr); + pri->ctl_addr = NULL; + kfree(pri->local_addr); + pri->local_addr = NULL; +} + +int daemon_user_write(int fd, void *buf, int len, struct daemon_data *pri) +{ + struct sockaddr_un *data_addr = pri->data_addr; + + return net_sendto(fd, buf, len, data_addr, sizeof(*data_addr)); +} + +const struct net_user_info daemon_user_info = { + .init = daemon_user_init, + .open = daemon_open, + .close = NULL, + .remove = daemon_remove, + .add_address = NULL, + .delete_address = NULL, + .mtu = ETH_MAX_PACKET, + .max_packet = ETH_MAX_PACKET + ETH_HEADER_OTHER, +}; |