1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320
|
/*
* libudev - interface to udev device information
*
* Copyright (C) 2008 Kay Sievers <kay.sievers@vrfy.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*/
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include "libudev.h"
#include "libudev-private.h"
/* wire protocol magic must match */
#define UDEV_CTRL_MAGIC 0xdead1dea
enum udev_ctrl_msg_type {
UDEV_CTRL_UNKNOWN,
UDEV_CTRL_SET_LOG_LEVEL,
UDEV_CTRL_STOP_EXEC_QUEUE,
UDEV_CTRL_START_EXEC_QUEUE,
UDEV_CTRL_RELOAD_RULES,
UDEV_CTRL_SET_ENV,
UDEV_CTRL_SET_CHILDREN_MAX,
UDEV_CTRL_SETTLE,
};
struct udev_ctrl_msg_wire {
char version[16];
unsigned int magic;
enum udev_ctrl_msg_type type;
union {
int intval;
char buf[256];
};
};
struct udev_ctrl_msg {
int refcount;
struct udev_ctrl *uctrl;
struct udev_ctrl_msg_wire ctrl_msg_wire;
pid_t pid;
};
struct udev_ctrl {
int refcount;
struct udev *udev;
int sock;
struct sockaddr_un saddr;
socklen_t addrlen;
};
struct udev_ctrl *udev_ctrl_new_from_socket(struct udev *udev, const char *socket_path)
{
struct udev_ctrl *uctrl;
uctrl = calloc(1, sizeof(struct udev_ctrl));
if (uctrl == NULL)
return NULL;
uctrl->refcount = 1;
uctrl->udev = udev;
uctrl->sock = socket(AF_LOCAL, SOCK_DGRAM, 0);
if (uctrl->sock < 0) {
err(udev, "error getting socket: %m\n");
udev_ctrl_unref(uctrl);
return NULL;
}
uctrl->saddr.sun_family = AF_LOCAL;
strcpy(uctrl->saddr.sun_path, socket_path);
uctrl->addrlen = offsetof(struct sockaddr_un, sun_path) + strlen(uctrl->saddr.sun_path);
/* translate leading '@' to abstract namespace */
if (uctrl->saddr.sun_path[0] == '@')
uctrl->saddr.sun_path[0] = '\0';
return uctrl;
}
int udev_ctrl_enable_receiving(struct udev_ctrl *uctrl)
{
int err;
const int feature_on = 1;
err= bind(uctrl->sock, (struct sockaddr *)&uctrl->saddr, uctrl->addrlen);
if (err < 0) {
err(uctrl->udev, "bind failed: %m\n");
return err;
}
/* enable receiving of the sender credentials */
setsockopt(uctrl->sock, SOL_SOCKET, SO_PASSCRED, &feature_on, sizeof(feature_on));
return 0;
}
struct udev *udev_ctrl_get_udev(struct udev_ctrl *uctrl)
{
return uctrl->udev;
}
struct udev_ctrl *udev_ctrl_ref(struct udev_ctrl *uctrl)
{
if (uctrl == NULL)
return NULL;
uctrl->refcount++;
return uctrl;
}
void udev_ctrl_unref(struct udev_ctrl *uctrl)
{
if (uctrl == NULL)
return;
uctrl->refcount--;
if (uctrl->refcount > 0)
return;
if (uctrl->sock >= 0)
close(uctrl->sock);
free(uctrl);
}
int udev_ctrl_get_fd(struct udev_ctrl *uctrl)
{
if (uctrl == NULL)
return -1;
return uctrl->sock;
}
static int ctrl_send(struct udev_ctrl *uctrl, enum udev_ctrl_msg_type type, int intval, const char *buf)
{
struct udev_ctrl_msg_wire ctrl_msg_wire;
int err;
memset(&ctrl_msg_wire, 0x00, sizeof(struct udev_ctrl_msg_wire));
strcpy(ctrl_msg_wire.version, "udev-" VERSION);
ctrl_msg_wire.magic = UDEV_CTRL_MAGIC;
ctrl_msg_wire.type = type;
if (buf != NULL)
util_strscpy(ctrl_msg_wire.buf, sizeof(ctrl_msg_wire.buf), buf);
else
ctrl_msg_wire.intval = intval;
err = sendto(uctrl->sock, &ctrl_msg_wire, sizeof(ctrl_msg_wire), 0,
(struct sockaddr *)&uctrl->saddr, uctrl->addrlen);
if (err == -1) {
err(uctrl->udev, "error sending message: %m\n");
}
return err;
}
int udev_ctrl_send_set_log_level(struct udev_ctrl *uctrl, int priority)
{
return ctrl_send(uctrl, UDEV_CTRL_SET_LOG_LEVEL, priority, NULL);
}
int udev_ctrl_send_stop_exec_queue(struct udev_ctrl *uctrl)
{
return ctrl_send(uctrl, UDEV_CTRL_STOP_EXEC_QUEUE, 0, NULL);
}
int udev_ctrl_send_start_exec_queue(struct udev_ctrl *uctrl)
{
return ctrl_send(uctrl, UDEV_CTRL_START_EXEC_QUEUE, 0, NULL);
}
int udev_ctrl_send_reload_rules(struct udev_ctrl *uctrl)
{
return ctrl_send(uctrl, UDEV_CTRL_RELOAD_RULES, 0, NULL);
}
int udev_ctrl_send_set_env(struct udev_ctrl *uctrl, const char *key)
{
return ctrl_send(uctrl, UDEV_CTRL_SET_ENV, 0, key);
}
int udev_ctrl_send_set_children_max(struct udev_ctrl *uctrl, int count)
{
return ctrl_send(uctrl, UDEV_CTRL_SET_CHILDREN_MAX, count, NULL);
}
int udev_ctrl_send_settle(struct udev_ctrl *uctrl)
{
return ctrl_send(uctrl, UDEV_CTRL_SETTLE, 0, NULL);
}
struct udev_ctrl_msg *udev_ctrl_receive_msg(struct udev_ctrl *uctrl)
{
struct udev_ctrl_msg *uctrl_msg;
ssize_t size;
struct msghdr smsg;
struct cmsghdr *cmsg;
struct iovec iov;
struct ucred *cred;
char cred_msg[CMSG_SPACE(sizeof(struct ucred))];
uctrl_msg = calloc(1, sizeof(struct udev_ctrl_msg));
if (uctrl_msg == NULL)
return NULL;
uctrl_msg->refcount = 1;
uctrl_msg->uctrl = uctrl;
iov.iov_base = &uctrl_msg->ctrl_msg_wire;
iov.iov_len = sizeof(struct udev_ctrl_msg_wire);
memset(&smsg, 0x00, sizeof(struct msghdr));
smsg.msg_iov = &iov;
smsg.msg_iovlen = 1;
smsg.msg_control = cred_msg;
smsg.msg_controllen = sizeof(cred_msg);
size = recvmsg(uctrl->sock, &smsg, 0);
if (size < 0) {
err(uctrl->udev, "unable to receive user udevd message: %m\n");
goto err;
}
cmsg = CMSG_FIRSTHDR(&smsg);
cred = (struct ucred *) CMSG_DATA(cmsg);
if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) {
err(uctrl->udev, "no sender credentials received, message ignored\n");
goto err;
}
if (cred->uid != 0) {
err(uctrl->udev, "sender uid=%i, message ignored\n", cred->uid);
goto err;
}
uctrl_msg->pid = cred->pid;
if (uctrl_msg->ctrl_msg_wire.magic != UDEV_CTRL_MAGIC) {
err(uctrl->udev, "message magic 0x%08x doesn't match, ignore it\n", uctrl_msg->ctrl_msg_wire.magic);
goto err;
}
dbg(uctrl->udev, "created ctrl_msg %p (%i)\n", uctrl_msg, uctrl_msg->ctrl_msg_wire.type);
return uctrl_msg;
err:
udev_ctrl_msg_unref(uctrl_msg);
return NULL;
}
struct udev_ctrl_msg *udev_ctrl_msg_ref(struct udev_ctrl_msg *ctrl_msg)
{
if (ctrl_msg == NULL)
return NULL;
ctrl_msg->refcount++;
return ctrl_msg;
}
void udev_ctrl_msg_unref(struct udev_ctrl_msg *ctrl_msg)
{
if (ctrl_msg == NULL)
return;
ctrl_msg->refcount--;
if (ctrl_msg->refcount > 0)
return;
dbg(ctrl_msg->uctrl->udev, "release ctrl_msg %p\n", ctrl_msg);
free(ctrl_msg);
}
int udev_ctrl_get_set_log_level(struct udev_ctrl_msg *ctrl_msg)
{
if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_SET_LOG_LEVEL)
return ctrl_msg->ctrl_msg_wire.intval;
return -1;
}
int udev_ctrl_get_stop_exec_queue(struct udev_ctrl_msg *ctrl_msg)
{
if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_STOP_EXEC_QUEUE)
return 1;
return -1;
}
int udev_ctrl_get_start_exec_queue(struct udev_ctrl_msg *ctrl_msg)
{
if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_START_EXEC_QUEUE)
return 1;
return -1;
}
int udev_ctrl_get_reload_rules(struct udev_ctrl_msg *ctrl_msg)
{
if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_RELOAD_RULES)
return 1;
return -1;
}
const char *udev_ctrl_get_set_env(struct udev_ctrl_msg *ctrl_msg)
{
if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_SET_ENV)
return ctrl_msg->ctrl_msg_wire.buf;
return NULL;
}
int udev_ctrl_get_set_children_max(struct udev_ctrl_msg *ctrl_msg)
{
if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_SET_CHILDREN_MAX)
return ctrl_msg->ctrl_msg_wire.intval;
return -1;
}
pid_t udev_ctrl_get_settle(struct udev_ctrl_msg *ctrl_msg)
{
if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_SETTLE)
return ctrl_msg->pid;
return -1;
}
|