/* * Copyright (c) 2010 The DragonFly Project. All rights reserved. * * This code is derived from software contributed to The DragonFly Project * by Alex Hornung * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * 3. Neither the name of The DragonFly Project nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific, prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define LIBDEVATTR_INTERNAL #include "devattr.h" struct udev { int gp_fd; int monitor_fd; int refs; void *userdata; }; struct udev * udev_ref(struct udev *udev_ctx) { atomic_add_int(&udev_ctx->refs, 1); return udev_ctx; } void udev_unref(struct udev *udev_ctx) { int refcount; refcount = atomic_fetchadd_int(&udev_ctx->refs, -1); if (refcount == 1) { atomic_subtract_int(&udev_ctx->refs, 0x400); /* in destruction */ if (udev_ctx->gp_fd != -1) close (udev_ctx->gp_fd); if (udev_ctx->monitor_fd != -1) close (udev_ctx->monitor_fd); free(udev_ctx); } } struct udev * udev_new(void) { struct udev *udev_ctx; int ret, s; ret = conn_local_server(LISTEN_SOCKET_FILE, SOCK_STREAM, 0, &s); if (ret < 0) return NULL; udev_ctx = malloc(sizeof(struct udev)); udev_ctx->refs = 1; udev_ctx->gp_fd = s; udev_ctx->monitor_fd = -1; udev_ctx->userdata = NULL; return udev_ctx; } const char *udev_get_dev_path(struct udev *udev_ctx __unused) { return "/dev"; } void * udev_get_userdata(struct udev *udev_ctx) { return udev_ctx->userdata; } void udev_set_userdata(struct udev *udev_ctx, void *userdata) { udev_ctx->userdata = userdata; } int udev_get_fd(struct udev *udev_ctx) { return udev_ctx->gp_fd; } int send_xml(int s, char *xml) { ssize_t r,n; size_t sz; sz = strlen(xml) + 1; r = send(s, &sz, sizeof(sz), 0); if (r <= 0) return r; r = 0; while (r < (ssize_t)sz) { n = send(s, xml+r, sz-r, 0); if (n <= 0) return n; r += n; } return r; } int read_xml(int s, char **buf) { char *xml; size_t sz; int n, r; *buf = NULL; n = recv(s, &sz, sizeof(sz), MSG_WAITALL); if ((n <= 0) || (sz > 12*1024*1024)) /* Arbitrary limit */ return n; xml = malloc(sz+2); r = 0; while (r < (ssize_t)sz) { n = recv(s, xml+r, sz-r, MSG_WAITALL); if (n <= 0) { free(xml); return n; } r += n; } *buf = xml; return r; } int _udev_dict_set_cstr(prop_dictionary_t dict, const char *key, char *str) { prop_string_t ps; ps = prop_string_create_cstring(str); if (ps == NULL) return ENOMEM; if (prop_dictionary_set(dict, key, ps) == false) { prop_object_release(ps); return ENOMEM; } prop_object_release(ps); return 0; } int _udev_dict_set_int(prop_dictionary_t dict, const char *key, int64_t val) { prop_number_t pn; pn = prop_number_create_integer(val); if (pn == NULL) return ENOMEM; if (prop_dictionary_set(dict, key, pn) == false) { prop_object_release(pn); return ENOMEM; } prop_object_release(pn); return 0; } int _udev_dict_set_uint(prop_dictionary_t dict, const char *key, uint64_t val) { prop_number_t pn; pn = prop_number_create_unsigned_integer(val); if (pn == NULL) return ENOMEM; if (prop_dictionary_set(dict, key, pn) == false) { prop_object_release(pn); return ENOMEM; } prop_object_release(pn); return 0; } int conn_local_server(const char *sockfile, int socktype, int nonblock __unused, int *retsock) { int s; struct sockaddr_un serv_addr; *retsock = -1; if ((s = socket(AF_UNIX, socktype, 0)) < 0) return -1; memset(&serv_addr, 0, sizeof(serv_addr)); serv_addr.sun_family = AF_UNIX; strncpy(serv_addr.sun_path, sockfile, SOCKFILE_NAMELEN); serv_addr.sun_path[SOCKFILE_NAMELEN - 1] = '\0'; *retsock = s; return connect(s, (struct sockaddr *)&serv_addr, sizeof(serv_addr)); } prop_dictionary_t udevd_get_command_dict(char *command) { prop_dictionary_t dict; int error; dict = prop_dictionary_create(); if (dict == NULL) return NULL; if ((error = _udev_dict_set_cstr(dict, "command", command))) goto error_out; return dict; error_out: prop_object_release(dict); return NULL; } prop_array_t udevd_request_devs(int s, prop_array_t filters) { prop_array_t pa; prop_dictionary_t dict; char *xml; int n; dict = udevd_get_command_dict(__DECONST(char *, "getdevs")); if (dict == NULL) return NULL; /* Add filters to message, if available */ if (filters != NULL) { if (prop_dictionary_set(dict, "filters", filters) == false) { prop_object_release(dict); return NULL; } } xml = prop_dictionary_externalize(dict); prop_object_release(dict); if (xml == NULL) return NULL; n = send_xml(s, xml); free(xml); if (n <= 0) return NULL; if ((n = read_xml(s, &xml)) <= 0) return NULL; xml[n+1] = '\0'; pa = prop_array_internalize(xml); free(xml); return (pa); }