1 /* 2 * Copyright (c) 2010 The DragonFly Project. All rights reserved. 3 * 4 * This code is derived from software contributed to The DragonFly Project 5 * by Alex Hornung <ahornung@gmail.com> 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the 16 * distribution. 17 * 3. Neither the name of The DragonFly Project nor the names of its 18 * contributors may be used to endorse or promote products derived 19 * from this software without specific, prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 #include <sys/types.h> 35 #include <sys/wait.h> 36 #include <sys/socket.h> 37 #include <sys/ioctl.h> 38 #include <sys/poll.h> 39 #include <sys/queue.h> 40 #include <sys/un.h> 41 42 #include <err.h> 43 #include <errno.h> 44 #include <fcntl.h> 45 #include <libgen.h> 46 #include <regex.h> 47 #include <signal.h> 48 #include <stdarg.h> 49 #include <stdio.h> 50 #include <stdlib.h> 51 #include <string.h> 52 #include <syslog.h> 53 #include <unistd.h> 54 55 #include <libprop/proplib.h> 56 #include <sys/udev.h> 57 #define LIBDEVATTR_INTERNAL 58 #include "devattr.h" 59 60 struct udev { 61 int gp_fd; 62 int monitor_fd; 63 int refs; 64 65 void *userdata; 66 }; 67 68 struct udev * 69 udev_ref(struct udev *udev_ctx) 70 { 71 atomic_add_int(&udev_ctx->refs, 1); 72 73 return udev_ctx; 74 } 75 76 void 77 udev_unref(struct udev *udev_ctx) 78 { 79 int refcount; 80 81 refcount = atomic_fetchadd_int(&udev_ctx->refs, -1); 82 83 if (refcount == 1) { 84 atomic_subtract_int(&udev_ctx->refs, 0x400); /* in destruction */ 85 if (udev_ctx->gp_fd != -1) 86 close (udev_ctx->gp_fd); 87 if (udev_ctx->monitor_fd != -1) 88 close (udev_ctx->monitor_fd); 89 90 free(udev_ctx); 91 } 92 } 93 94 struct udev * 95 udev_new(void) 96 { 97 struct udev *udev_ctx; 98 int ret, s; 99 100 ret = conn_local_server(LISTEN_SOCKET_FILE, SOCK_STREAM, 0, &s); 101 if (ret < 0) 102 return NULL; 103 104 udev_ctx = malloc(sizeof(struct udev)); 105 106 udev_ctx->refs = 1; 107 udev_ctx->gp_fd = s; 108 udev_ctx->monitor_fd = -1; 109 udev_ctx->userdata = NULL; 110 111 return udev_ctx; 112 } 113 114 const char *udev_get_dev_path(struct udev *udev_ctx __unused) 115 { 116 return "/dev"; 117 } 118 119 void * 120 udev_get_userdata(struct udev *udev_ctx) 121 { 122 return udev_ctx->userdata; 123 } 124 125 void 126 udev_set_userdata(struct udev *udev_ctx, void *userdata) 127 { 128 udev_ctx->userdata = userdata; 129 } 130 131 int 132 udev_get_fd(struct udev *udev_ctx) 133 { 134 return udev_ctx->gp_fd; 135 } 136 137 int 138 send_xml(int s, char *xml) 139 { 140 ssize_t r,n; 141 size_t sz; 142 143 sz = strlen(xml) + 1; 144 145 r = send(s, &sz, sizeof(sz), 0); 146 if (r <= 0) 147 return r; 148 149 r = 0; 150 while (r < (ssize_t)sz) { 151 n = send(s, xml+r, sz-r, 0); 152 if (n <= 0) 153 return n; 154 r += n; 155 } 156 157 return r; 158 } 159 160 int 161 read_xml(int s, char **buf) 162 { 163 char *xml; 164 size_t sz; 165 int n, r; 166 167 *buf = NULL; 168 169 n = recv(s, &sz, sizeof(sz), MSG_WAITALL); 170 if ((n <= 0) || (sz > 12*1024*1024)) /* Arbitrary limit */ 171 return n; 172 173 xml = malloc(sz+2); 174 r = 0; 175 while (r < (ssize_t)sz) { 176 n = recv(s, xml+r, sz-r, MSG_WAITALL); 177 if (n <= 0) { 178 free(xml); 179 return n; 180 } 181 r += n; 182 } 183 184 *buf = xml; 185 return r; 186 } 187 188 int 189 _udev_dict_set_cstr(prop_dictionary_t dict, const char *key, char *str) 190 { 191 prop_string_t ps; 192 193 ps = prop_string_create_cstring(str); 194 if (ps == NULL) 195 return ENOMEM; 196 197 if (prop_dictionary_set(dict, key, ps) == false) { 198 prop_object_release(ps); 199 return ENOMEM; 200 } 201 202 prop_object_release(ps); 203 return 0; 204 } 205 206 int 207 _udev_dict_set_int(prop_dictionary_t dict, const char *key, int64_t val) 208 { 209 prop_number_t pn; 210 211 pn = prop_number_create_integer(val); 212 if (pn == NULL) 213 return ENOMEM; 214 215 if (prop_dictionary_set(dict, key, pn) == false) { 216 prop_object_release(pn); 217 return ENOMEM; 218 } 219 220 prop_object_release(pn); 221 return 0; 222 } 223 224 int 225 _udev_dict_set_uint(prop_dictionary_t dict, const char *key, uint64_t val) 226 { 227 prop_number_t pn; 228 229 pn = prop_number_create_unsigned_integer(val); 230 if (pn == NULL) 231 return ENOMEM; 232 233 if (prop_dictionary_set(dict, key, pn) == false) { 234 prop_object_release(pn); 235 return ENOMEM; 236 } 237 238 prop_object_release(pn); 239 return 0; 240 } 241 242 int 243 conn_local_server(const char *sockfile, int socktype, int nonblock __unused, 244 int *retsock) 245 { 246 int s; 247 struct sockaddr_un serv_addr; 248 249 *retsock = -1; 250 if ((s = socket(AF_UNIX, socktype, 0)) < 0) 251 return -1; 252 253 memset(&serv_addr, 0, sizeof(serv_addr)); 254 serv_addr.sun_family = AF_UNIX; 255 strncpy(serv_addr.sun_path, sockfile, SOCKFILE_NAMELEN); 256 serv_addr.sun_path[SOCKFILE_NAMELEN - 1] = '\0'; 257 258 *retsock = s; 259 return connect(s, (struct sockaddr *)&serv_addr, sizeof(serv_addr)); 260 } 261 262 prop_dictionary_t 263 udevd_get_command_dict(char *command) 264 { 265 prop_dictionary_t dict; 266 int error; 267 268 dict = prop_dictionary_create(); 269 if (dict == NULL) 270 return NULL; 271 272 if ((error = _udev_dict_set_cstr(dict, "command", command))) 273 goto error_out; 274 275 return dict; 276 277 error_out: 278 prop_object_release(dict); 279 return NULL; 280 } 281 282 prop_array_t 283 udevd_request_devs(int s, prop_array_t filters) 284 { 285 prop_array_t pa; 286 prop_dictionary_t dict; 287 char *xml; 288 289 int n; 290 291 dict = udevd_get_command_dict(__DECONST(char *, "getdevs")); 292 if (dict == NULL) 293 return NULL; 294 295 /* Add filters to message, if available */ 296 if (filters != NULL) { 297 if (prop_dictionary_set(dict, "filters", filters) == false) { 298 prop_object_release(dict); 299 return NULL; 300 } 301 } 302 303 xml = prop_dictionary_externalize(dict); 304 prop_object_release(dict); 305 if (xml == NULL) 306 return NULL; 307 308 n = send_xml(s, xml); 309 free(xml); 310 311 if (n <= 0) 312 return NULL; 313 314 if ((n = read_xml(s, &xml)) <= 0) 315 return NULL; 316 317 xml[n+1] = '\0'; 318 pa = prop_array_internalize(xml); 319 free(xml); 320 return (pa); 321 } 322