hotplug.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640
  1. /*
  2. * Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
  3. * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU Lesser General Public License version 2.1
  7. * as published by the Free Software Foundation
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. */
  14. #include <sys/stat.h>
  15. #include <sys/socket.h>
  16. #include <sys/types.h>
  17. #include <sys/sysmacros.h>
  18. #include <linux/types.h>
  19. #include <linux/netlink.h>
  20. #include <libubox/blobmsg_json.h>
  21. #include <libubox/json_script.h>
  22. #include <libubox/uloop.h>
  23. #include <json-c/json.h>
  24. #include <errno.h>
  25. #include <fcntl.h>
  26. #include <unistd.h>
  27. #include <stdlib.h>
  28. #include <libgen.h>
  29. #include <grp.h>
  30. #include "../procd.h"
  31. #include "hotplug.h"
  32. #define HOTPLUG_WAIT 500
  33. struct cmd_handler;
  34. struct cmd_queue {
  35. struct list_head list;
  36. struct blob_attr *msg;
  37. struct blob_attr *data;
  38. int timeout;
  39. void (*handler)(struct blob_attr *msg, struct blob_attr *data);
  40. void (*start)(struct blob_attr *msg, struct blob_attr *data);
  41. void (*complete)(struct blob_attr *msg, struct blob_attr *data, int ret);
  42. };
  43. struct button_timeout {
  44. struct list_head list;
  45. struct uloop_timeout timeout;
  46. char *name;
  47. int seen;
  48. struct blob_attr *data;
  49. };
  50. static LIST_HEAD(cmd_queue);
  51. static LIST_HEAD(button_timer);
  52. static struct uloop_process queue_proc;
  53. static struct uloop_timeout last_event;
  54. static struct blob_buf b, button_buf;
  55. static char *rule_file;
  56. static struct blob_buf script;
  57. static struct cmd_queue *current;
  58. static void queue_add(struct cmd_handler *h, struct blob_attr *msg, struct blob_attr *data);
  59. static void handle_button_complete(struct blob_attr *msg, struct blob_attr *data, int ret);
  60. static void button_free(struct button_timeout *b)
  61. {
  62. uloop_timeout_cancel(&b->timeout);
  63. list_del(&b->list);
  64. free(b->data);
  65. free(b->name);
  66. free(b);
  67. }
  68. static void button_timeout_remove(char *button)
  69. {
  70. struct button_timeout *b, *c;
  71. if (!list_empty(&button_timer)) list_for_each_entry_safe(b, c, &button_timer, list) {
  72. if (!strcmp(b->name, button))
  73. button_free(b);
  74. }
  75. }
  76. static char *hotplug_msg_find_var(struct blob_attr *msg, const char *name)
  77. {
  78. struct blob_attr *cur;
  79. int rem;
  80. blobmsg_for_each_attr(cur, msg, rem) {
  81. if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING)
  82. continue;
  83. if (strcmp(blobmsg_name(cur), name) != 0)
  84. continue;
  85. return blobmsg_data(cur);
  86. }
  87. return NULL;
  88. }
  89. static void mkdir_p(char *dir)
  90. {
  91. char *l = strrchr(dir, '/');
  92. if (l) {
  93. *l = '\0';
  94. mkdir_p(dir);
  95. *l = '/';
  96. mkdir(dir, 0755);
  97. }
  98. }
  99. static void chgrp_error(const char *group, const char *target, const char *failed)
  100. {
  101. ERROR("cannot set group %s for %s (%s: %d)\n",
  102. group, target, failed, errno);
  103. }
  104. static void chgrp_target(struct blob_attr *bgroup, struct blob_attr *btarget)
  105. {
  106. int ret = 0;
  107. struct group *g = NULL;
  108. const char *group = blobmsg_get_string(bgroup);
  109. const char *target = blobmsg_get_string(btarget);
  110. errno = 0;
  111. g = getgrnam(group);
  112. if (!g)
  113. return chgrp_error(group, target, "getgrnam");
  114. ret = chown(target, 0, g->gr_gid);
  115. if (ret < 0)
  116. return chgrp_error(group, target, "chown");
  117. }
  118. static void handle_makedev(struct blob_attr *msg, struct blob_attr *data)
  119. {
  120. unsigned int oldumask = umask(0);
  121. static struct blobmsg_policy mkdev_policy[3] = {
  122. { .type = BLOBMSG_TYPE_STRING },
  123. { .type = BLOBMSG_TYPE_STRING },
  124. { .type = BLOBMSG_TYPE_STRING },
  125. };
  126. struct blob_attr *tb[3];
  127. char *minor = hotplug_msg_find_var(msg, "MINOR");
  128. char *major = hotplug_msg_find_var(msg, "MAJOR");
  129. char *subsystem = hotplug_msg_find_var(msg, "SUBSYSTEM");
  130. blobmsg_parse_array(mkdev_policy, 3, tb, blobmsg_data(data), blobmsg_data_len(data));
  131. if (tb[0] && tb[1] && minor && major && subsystem) {
  132. mode_t m = S_IFCHR;
  133. char *d = strdup(blobmsg_get_string(tb[0]));
  134. d = dirname(d);
  135. mkdir_p(d);
  136. free(d);
  137. if (!strcmp(subsystem, "block"))
  138. m = S_IFBLK;
  139. mknod(blobmsg_get_string(tb[0]),
  140. m | strtoul(blobmsg_data(tb[1]), NULL, 8),
  141. makedev(atoi(major), atoi(minor)));
  142. if (tb[2])
  143. chgrp_target(tb[2], tb[0]);
  144. }
  145. umask(oldumask);
  146. }
  147. static void handle_rm(struct blob_attr *msg, struct blob_attr *data)
  148. {
  149. static struct blobmsg_policy rm_policy = {
  150. .type = BLOBMSG_TYPE_STRING,
  151. };
  152. struct blob_attr *tb;
  153. blobmsg_parse_array(&rm_policy, 1, &tb, blobmsg_data(data), blobmsg_data_len(data));
  154. if (tb)
  155. unlink(blobmsg_data(tb));
  156. }
  157. static void handle_exec(struct blob_attr *msg, struct blob_attr *data)
  158. {
  159. char *argv[8];
  160. struct blob_attr *cur;
  161. int rem, fd;
  162. int i = 0;
  163. blobmsg_for_each_attr(cur, msg, rem)
  164. setenv(blobmsg_name(cur), blobmsg_data(cur), 1);
  165. blobmsg_for_each_attr(cur, data, rem) {
  166. argv[i] = blobmsg_data(cur);
  167. i++;
  168. if (i == 7)
  169. break;
  170. }
  171. if (debug < 3) {
  172. fd = open("/dev/null", O_RDWR);
  173. if (fd > -1) {
  174. dup2(fd, STDIN_FILENO);
  175. dup2(fd, STDOUT_FILENO);
  176. dup2(fd, STDERR_FILENO);
  177. if (fd > STDERR_FILENO)
  178. close(fd);
  179. }
  180. }
  181. if (i > 0) {
  182. argv[i] = NULL;
  183. execvp(argv[0], &argv[0]);
  184. }
  185. exit(-1);
  186. }
  187. static void handle_button_start(struct blob_attr *msg, struct blob_attr *data)
  188. {
  189. char *button = hotplug_msg_find_var(msg, "BUTTON");
  190. if (button)
  191. button_timeout_remove(button);
  192. }
  193. static void handle_firmware(struct blob_attr *msg, struct blob_attr *data)
  194. {
  195. char *dir = blobmsg_get_string(blobmsg_data(data));
  196. char *file = hotplug_msg_find_var(msg, "FIRMWARE");
  197. char *dev = hotplug_msg_find_var(msg, "DEVPATH");
  198. struct stat s = { 0 };
  199. char *path, loadpath[256], syspath[256];
  200. int fw, src, load, len;
  201. static char buf[4096];
  202. DEBUG(2, "Firmware request for %s/%s\n", dir, file);
  203. if (!file || !dir || !dev) {
  204. ERROR("Request for unknown firmware %s/%s\n", dir, file);
  205. exit(-1);
  206. }
  207. path = alloca(strlen(dir) + strlen(file) + 2);
  208. sprintf(path, "%s/%s", dir, file);
  209. if (stat(path, &s)) {
  210. ERROR("Could not find firmware %s: %m\n", path);
  211. src = -1;
  212. s.st_size = 0;
  213. goto send_to_kernel;
  214. }
  215. src = open(path, O_RDONLY);
  216. if (src < 0) {
  217. ERROR("Failed to open %s: %m\n", path);
  218. s.st_size = 0;
  219. goto send_to_kernel;
  220. }
  221. send_to_kernel:
  222. snprintf(loadpath, sizeof(loadpath), "/sys/%s/loading", dev);
  223. load = open(loadpath, O_WRONLY);
  224. if (!load) {
  225. ERROR("Failed to open %s: %m\n", loadpath);
  226. exit(-1);
  227. }
  228. if (write(load, "1", 1) == -1) {
  229. ERROR("Failed to write to %s: %m\n", loadpath);
  230. exit(-1);
  231. }
  232. close(load);
  233. snprintf(syspath, sizeof(syspath), "/sys/%s/data", dev);
  234. fw = open(syspath, O_WRONLY);
  235. if (fw < 0) {
  236. ERROR("Failed to open %s: %m\n", syspath);
  237. exit(-1);
  238. }
  239. len = s.st_size;
  240. while (len) {
  241. len = read(src, buf, sizeof(buf));
  242. if (len <= 0)
  243. break;
  244. if (write(fw, buf, len) == -1) {
  245. ERROR("failed to write firmware file %s/%s to %s: %m\n", dir, file, dev);
  246. break;
  247. }
  248. }
  249. if (src >= 0)
  250. close(src);
  251. close(fw);
  252. load = open(loadpath, O_WRONLY);
  253. if (write(load, "0", 1) == -1)
  254. ERROR("failed to write to %s: %m\n", loadpath);
  255. close(load);
  256. DEBUG(2, "Done loading %s\n", path);
  257. exit(-1);
  258. }
  259. enum {
  260. HANDLER_MKDEV = 0,
  261. HANDLER_RM,
  262. HANDLER_EXEC,
  263. HANDLER_BUTTON,
  264. HANDLER_FW,
  265. };
  266. static struct cmd_handler {
  267. char *name;
  268. int atomic;
  269. void (*handler)(struct blob_attr *msg, struct blob_attr *data);
  270. void (*start)(struct blob_attr *msg, struct blob_attr *data);
  271. void (*complete)(struct blob_attr *msg, struct blob_attr *data, int ret);
  272. } handlers[] = {
  273. [HANDLER_MKDEV] = {
  274. .name = "makedev",
  275. .atomic = 1,
  276. .handler = handle_makedev,
  277. },
  278. [HANDLER_RM] = {
  279. .name = "rm",
  280. .atomic = 1,
  281. .handler = handle_rm,
  282. },
  283. [HANDLER_EXEC] = {
  284. .name = "exec",
  285. .handler = handle_exec,
  286. },
  287. [HANDLER_BUTTON] = {
  288. .name = "button",
  289. .handler = handle_exec,
  290. .start = handle_button_start,
  291. .complete = handle_button_complete,
  292. },
  293. [HANDLER_FW] = {
  294. .name = "load-firmware",
  295. .handler = handle_firmware,
  296. },
  297. };
  298. static void queue_next(void)
  299. {
  300. struct cmd_queue *c;
  301. if (queue_proc.pending || list_empty(&cmd_queue))
  302. return;
  303. c = list_first_entry(&cmd_queue, struct cmd_queue, list);
  304. queue_proc.pid = fork();
  305. if (!queue_proc.pid) {
  306. uloop_done();
  307. c->handler(c->msg, c->data);
  308. exit(0);
  309. }
  310. if (c->start)
  311. c->start(c->msg, c->data);
  312. list_del(&c->list);
  313. if (c->complete)
  314. current = c;
  315. else
  316. free(c);
  317. if (queue_proc.pid <= 0) {
  318. queue_next();
  319. return;
  320. }
  321. uloop_process_add(&queue_proc);
  322. DEBUG(4, "Launched hotplug exec instance, pid=%d\n", (int) queue_proc.pid);
  323. }
  324. static void queue_proc_cb(struct uloop_process *c, int ret)
  325. {
  326. DEBUG(4, "Finished hotplug exec instance, pid=%d\n", (int) c->pid);
  327. if (current) {
  328. current->complete(current->msg, current->data, ret);
  329. free(current);
  330. current = NULL;
  331. }
  332. queue_next();
  333. }
  334. static void queue_add(struct cmd_handler *h, struct blob_attr *msg, struct blob_attr *data)
  335. {
  336. struct cmd_queue *c = NULL;
  337. struct blob_attr *_msg, *_data;
  338. c = calloc_a(sizeof(struct cmd_queue),
  339. &_msg, blob_pad_len(msg),
  340. &_data, blob_pad_len(data),
  341. NULL);
  342. if (!c)
  343. return;
  344. c->msg = _msg;
  345. c->data = _data;
  346. memcpy(c->msg, msg, blob_pad_len(msg));
  347. memcpy(c->data, data, blob_pad_len(data));
  348. c->handler = h->handler;
  349. c->complete = h->complete;
  350. c->start = h->start;
  351. list_add_tail(&c->list, &cmd_queue);
  352. queue_next();
  353. }
  354. static void handle_button_timeout(struct uloop_timeout *t)
  355. {
  356. struct button_timeout *b;
  357. char seen[16];
  358. b = container_of(t, struct button_timeout, timeout);
  359. blob_buf_init(&button_buf, 0);
  360. blobmsg_add_string(&button_buf, "BUTTON", b->name);
  361. blobmsg_add_string(&button_buf, "ACTION", "timeout");
  362. snprintf(seen, sizeof(seen), "%d", b->seen);
  363. blobmsg_add_string(&button_buf, "SEEN", seen);
  364. queue_add(&handlers[HANDLER_EXEC], button_buf.head, b->data);
  365. button_free(b);
  366. }
  367. static void handle_button_complete(struct blob_attr *msg, struct blob_attr *data, int ret)
  368. {
  369. char *name = hotplug_msg_find_var(msg, "BUTTON");
  370. struct button_timeout *b;
  371. int timeout = ret >> 8;
  372. if (!timeout)
  373. return;
  374. if (!name)
  375. return;
  376. b = calloc(1, sizeof(*b));
  377. if (!b)
  378. return;
  379. b->data = malloc(blob_pad_len(data));
  380. b->name = strdup(name);
  381. b->seen = timeout;
  382. memcpy(b->data, data, blob_pad_len(data));
  383. b->timeout.cb = handle_button_timeout;
  384. uloop_timeout_set(&b->timeout, timeout * 1000);
  385. list_add(&b->list, &button_timer);
  386. }
  387. static const char* rule_handle_var(struct json_script_ctx *ctx, const char *name, struct blob_attr *vars)
  388. {
  389. const char *str, *sep;
  390. if (!strcmp(name, "DEVICENAME") || !strcmp(name, "DEVNAME")) {
  391. str = json_script_find_var(ctx, vars, "DEVPATH");
  392. if (!str)
  393. return NULL;
  394. sep = strrchr(str, '/');
  395. if (sep)
  396. return sep + 1;
  397. return str;
  398. }
  399. return NULL;
  400. }
  401. static struct json_script_file *
  402. rule_handle_file(struct json_script_ctx *ctx, const char *name)
  403. {
  404. json_object *obj;
  405. obj = json_object_from_file((char*)name);
  406. if (!obj)
  407. return NULL;
  408. blob_buf_init(&script, 0);
  409. blobmsg_add_json_element(&script, "", obj);
  410. return json_script_file_from_blobmsg(name, blob_data(script.head), blob_len(script.head));
  411. }
  412. static void rule_handle_command(struct json_script_ctx *ctx, const char *name,
  413. struct blob_attr *data, struct blob_attr *vars)
  414. {
  415. struct blob_attr *cur;
  416. int rem, i;
  417. if (debug > 3) {
  418. DEBUG(4, "Command: %s\n", name);
  419. blobmsg_for_each_attr(cur, data, rem)
  420. DEBUG(4, " %s\n", (char *) blobmsg_data(cur));
  421. DEBUG(4, "Message:\n");
  422. blobmsg_for_each_attr(cur, vars, rem)
  423. DEBUG(4, " %s=%s\n", blobmsg_name(cur), (char *) blobmsg_data(cur));
  424. }
  425. for (i = 0; i < ARRAY_SIZE(handlers); i++)
  426. if (!strcmp(handlers[i].name, name)) {
  427. if (handlers[i].atomic)
  428. handlers[i].handler(vars, data);
  429. else
  430. queue_add(&handlers[i], vars, data);
  431. break;
  432. }
  433. if (last_event.cb)
  434. uloop_timeout_set(&last_event, HOTPLUG_WAIT);
  435. }
  436. static void rule_handle_error(struct json_script_ctx *ctx, const char *msg,
  437. struct blob_attr *context)
  438. {
  439. char *s;
  440. s = blobmsg_format_json(context, false);
  441. ERROR("ERROR: %s in block: %s\n", msg, s);
  442. free(s);
  443. }
  444. static struct json_script_ctx jctx = {
  445. .handle_var = rule_handle_var,
  446. .handle_error = rule_handle_error,
  447. .handle_command = rule_handle_command,
  448. .handle_file = rule_handle_file,
  449. };
  450. static void hotplug_handler_debug(struct blob_attr *data)
  451. {
  452. char *str;
  453. if (debug < 3)
  454. return;
  455. str = blobmsg_format_json(data, true);
  456. DEBUG(3, "%s\n", str);
  457. free(str);
  458. }
  459. static void hotplug_handler(struct uloop_fd *u, unsigned int ev)
  460. {
  461. int i = 0;
  462. static char buf[4096];
  463. int len = recv(u->fd, buf, sizeof(buf) - 1, MSG_DONTWAIT);
  464. void *index;
  465. if (len < 1)
  466. return;
  467. buf[len] = '\0';
  468. blob_buf_init(&b, 0);
  469. index = blobmsg_open_table(&b, NULL);
  470. while (i < len) {
  471. int l = strlen(buf + i) + 1;
  472. char *e = strstr(&buf[i], "=");
  473. if (e) {
  474. *e = '\0';
  475. blobmsg_add_string(&b, &buf[i], &e[1]);
  476. }
  477. i += l;
  478. }
  479. blobmsg_close_table(&b, index);
  480. hotplug_handler_debug(b.head);
  481. json_script_run(&jctx, rule_file, blob_data(b.head));
  482. }
  483. static struct uloop_fd hotplug_fd = {
  484. .cb = hotplug_handler,
  485. };
  486. void hotplug_last_event(uloop_timeout_handler handler)
  487. {
  488. last_event.cb = handler;
  489. if (handler)
  490. uloop_timeout_set(&last_event, HOTPLUG_WAIT);
  491. else
  492. uloop_timeout_cancel(&last_event);
  493. }
  494. void hotplug(char *rules)
  495. {
  496. struct sockaddr_nl nls = {};
  497. int nlbufsize = 512 * 1024;
  498. rule_file = strdup(rules);
  499. nls.nl_family = AF_NETLINK;
  500. nls.nl_pid = getpid();
  501. nls.nl_groups = -1;
  502. if ((hotplug_fd.fd = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_KOBJECT_UEVENT)) == -1) {
  503. ERROR("Failed to open hotplug socket: %m\n");
  504. exit(1);
  505. }
  506. if (bind(hotplug_fd.fd, (void *)&nls, sizeof(struct sockaddr_nl))) {
  507. ERROR("Failed to bind hotplug socket: %m\n");
  508. exit(1);
  509. }
  510. if (setsockopt(hotplug_fd.fd, SOL_SOCKET, SO_RCVBUFFORCE, &nlbufsize, sizeof(nlbufsize)))
  511. ERROR("Failed to resize receive buffer: %m\n");
  512. json_script_init(&jctx);
  513. queue_proc.cb = queue_proc_cb;
  514. uloop_fd_add(&hotplug_fd, ULOOP_READ);
  515. }
  516. int hotplug_run(char *rules)
  517. {
  518. uloop_init();
  519. hotplug(rules);
  520. uloop_run();
  521. return 0;
  522. }
  523. void hotplug_shutdown(void)
  524. {
  525. uloop_fd_delete(&hotplug_fd);
  526. close(hotplug_fd.fd);
  527. }