diff options
author | Jeremy Kerr <jk@ozlabs.org> | 2007-04-02 10:31:10 +1000 |
---|---|---|
committer | Jeremy Kerr <jk@ozlabs.org> | 2007-04-02 10:31:10 +1000 |
commit | f60d0b2e7dbd9d85980866c68d0f87b6bc823663 (patch) | |
tree | e41787450d6ef61296ec33b0743275716bba5a95 /devices.c | |
download | talos-petitboot-f60d0b2e7dbd9d85980866c68d0f87b6bc823663.tar.gz talos-petitboot-f60d0b2e7dbd9d85980866c68d0f87b6bc823663.zip |
Initial gitification of petitboot
Start a git repo for petitboot, sources from Ben Herrenschmidt
and Jeremy Kerr.
Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Diffstat (limited to 'devices.c')
-rw-r--r-- | devices.c | 272 |
1 files changed, 272 insertions, 0 deletions
diff --git a/devices.c b/devices.c new file mode 100644 index 0000000..ac93287 --- /dev/null +++ b/devices.c @@ -0,0 +1,272 @@ +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <asm/byteorder.h> + +#include <libtwin/twin_png.h> +#include "petitboot.h" +#include "devices/message.h" + +#define PBOOT_DEVICE_SOCKET "/var/tmp/petitboot-dev" +#define PBOOT_DEFAULT_ICON "artwork/usbpen.png" + +static const char *default_icon = PBOOT_DEFAULT_ICON; + +struct discovery_context { + /* nothing at present */ + int pad; +} _ctx; + +struct device_context { + struct discovery_context *discovery_ctx; + uint8_t action; + int device_idx; +}; + +static twin_pixmap_t *get_icon(const char *filename) +{ + /* todo: cache */ + twin_pixmap_t *icon; + + if (!filename) + filename = default_icon; + +retry: + LOG("loading icon %s ... ", filename); + icon = twin_png_to_pixmap(filename, TWIN_ARGB32); + LOG("%s\n", icon ? "ok" : "failed"); + + if (!icon && filename != default_icon) { + filename = default_icon; + LOG("reverting to default icon %s\n", filename); + goto retry; + } + + return icon; +} + +#define MAX_LEN 4096 +static char *read_string(int fd) +{ + int len; + uint32_t len_buf; + char *str, *pos; + + if (read(fd, &len_buf, sizeof(len_buf)) != sizeof(len_buf)) { + perror("read"); + return NULL; + } + + len = __be32_to_cpu(len_buf); + if (len + 1 > MAX_LEN) { + /* todo: truncate instead */ + return NULL; + } + + pos = str = malloc(len + 1); + + while (len) { + int rc = read(fd, pos, len); + if (rc <= 0) { + free(str); + LOG("read failed: %s\n", strerror(errno)); + return NULL; + } + pos += rc; + len -= rc; + } + *pos = '\0'; + + return str; +} + +static int _read_strings(int fd, void *ptr, int n_strings) +{ + char **strings = ptr; + int i, ret = TWIN_TRUE; + + for (i = 0; i < n_strings; i++) { + strings[i] = read_string(fd); + if (!strings[i]) { + ret = TWIN_FALSE; + break; + } + } + + if (!ret) + while (i-- > 0) { + free(strings[i]); + strings[i] = NULL; + } + + return ret; +} + +static void _free_strings(void *ptr, int n_strings) +{ + char **strings = ptr; + int i; + + for (i = 0; i < n_strings; i++) + free(strings[i]); + +} + +#define n_strings(x) (sizeof((x)) / sizeof(char *)) +#define read_strings(f,x) _read_strings((f), &(x), n_strings(x)) +#define free_strings(x) _free_strings(&(x), n_strings(x)) + +static int read_action(int fd, uint8_t *action) +{ + return read(fd, action, sizeof(*action)) != sizeof(*action); +} + +static int read_device(int fd, struct device_context *dev_ctx) +{ + /* name, description, icon_file */ + struct device dev; + twin_pixmap_t *icon; + int index = -1; + + if (!read_strings(fd, dev)) + return TWIN_FALSE; + + LOG("got device: '%s'\n", dev.name); + + icon = get_icon(dev.icon_file); + + if (!icon) + goto out; + + index = dev_ctx->device_idx = pboot_add_device(dev.id, dev.name, icon); + +out: + free_strings(dev); + + return index != -1; +} + +static int read_option(int fd, struct device_context *dev_ctx) +{ + struct boot_option opt; + twin_pixmap_t *icon; + int index = -1; + + if (!read_strings(fd, opt)) + return TWIN_FALSE; + + LOG("got option: '%s'\n", opt.name); + icon = get_icon(opt.icon_file); + + if (icon) + index = pboot_add_option(dev_ctx->device_idx, opt.name, + opt.description, icon); + + free_strings(opt); + + return index != -1; +} + +static twin_bool_t pboot_proc_client_sock(int sock, twin_file_op_t ops, + void *closure) +{ + struct device_context *dev_ctx = closure; + uint8_t action; + + if (read_action(sock, &action)) + goto out_err; + + if (action == DEV_ACTION_ADD_DEVICE) { + if (!read_device(sock, dev_ctx)) + goto out_err; + + } else if (action == DEV_ACTION_ADD_OPTION) { + if (dev_ctx->device_idx == -1) { + LOG("option, but no device has been sent?\n"); + goto out_err; + } + + if (!read_option(sock, dev_ctx)) + goto out_err; + + } else if (action == DEV_ACTION_REMOVE_DEVICE) { + char *dev_id = read_string(sock); + if (!dev_id) + goto out_err; + + LOG("remove device %s\n", dev_id); + pboot_remove_device(dev_id); + + } else { + LOG("unsupported action %d\n", action); + goto out_err; + } + + return TWIN_TRUE; + +out_err: + close(sock); + return TWIN_FALSE; +} + +static twin_bool_t pboot_proc_server_sock(int sock, twin_file_op_t ops, + void *closure) +{ + int fd; + struct discovery_context *disc_ctx = closure; + struct device_context *dev_ctx; + + fd = accept(sock, NULL, 0); + if (fd < 0) { + LOG("accept failed: %s", strerror(errno)); + return TWIN_FALSE; + } + + dev_ctx = malloc(sizeof(*dev_ctx)); + dev_ctx->discovery_ctx = disc_ctx; + dev_ctx->device_idx = -1; + dev_ctx->action = 0xff; + + twin_set_file(pboot_proc_client_sock, fd, TWIN_READ, dev_ctx); + + return TWIN_TRUE; +} + +int pboot_start_device_discovery(void) +{ + int sock; + struct sockaddr_un addr; + + unlink(PBOOT_DEVICE_SOCKET); + + sock = socket(PF_UNIX, SOCK_STREAM, 0); + if (sock < 0) { + perror("socket"); + return TWIN_FALSE; + } + + addr.sun_family = AF_UNIX; + strcpy(addr.sun_path, PBOOT_DEVICE_SOCKET); + + if (bind(sock, (struct sockaddr *)&addr, sizeof(addr))) { + LOG("can't bind to %s: %s", addr.sun_path, strerror(errno)); + return TWIN_FALSE; + } + + if (listen(sock, 1)) { + LOG("can't listen on socket %s: %s", + addr.sun_path, strerror(errno)); + return TWIN_FALSE; + } + + LOG("listening on %s\n", addr.sun_path); + + twin_set_file(pboot_proc_server_sock, sock, TWIN_READ, &_ctx); + + return TWIN_TRUE; +} + |