hotplug.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659
  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(EXIT_FAILURE);
  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(EXIT_FAILURE);
  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(EXIT_FAILURE);
  227. }
  228. if (write(load, "1", 1) == -1) {
  229. ERROR("Failed to write to %s: %m\n", loadpath);
  230. exit(EXIT_FAILURE);
  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(EXIT_FAILURE);
  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(EXIT_FAILURE);
  258. }
  259. static void handle_start_console(struct blob_attr *msg, struct blob_attr *data)
  260. {
  261. char *dev = blobmsg_get_string(blobmsg_data(data));
  262. DEBUG(2, "Start console request for %s\n", dev);
  263. procd_inittab_run("respawn");
  264. procd_inittab_run("askfirst");
  265. DEBUG(2, "Done starting console for %s\n", dev);
  266. exit(EXIT_FAILURE);
  267. }
  268. enum {
  269. HANDLER_MKDEV = 0,
  270. HANDLER_RM,
  271. HANDLER_EXEC,
  272. HANDLER_BUTTON,
  273. HANDLER_FW,
  274. HANDLER_START_CONSOLE,
  275. };
  276. static struct cmd_handler {
  277. char *name;
  278. int atomic;
  279. void (*handler)(struct blob_attr *msg, struct blob_attr *data);
  280. void (*start)(struct blob_attr *msg, struct blob_attr *data);
  281. void (*complete)(struct blob_attr *msg, struct blob_attr *data, int ret);
  282. } handlers[] = {
  283. [HANDLER_MKDEV] = {
  284. .name = "makedev",
  285. .atomic = 1,
  286. .handler = handle_makedev,
  287. },
  288. [HANDLER_RM] = {
  289. .name = "rm",
  290. .atomic = 1,
  291. .handler = handle_rm,
  292. },
  293. [HANDLER_EXEC] = {
  294. .name = "exec",
  295. .handler = handle_exec,
  296. },
  297. [HANDLER_BUTTON] = {
  298. .name = "button",
  299. .handler = handle_exec,
  300. .start = handle_button_start,
  301. .complete = handle_button_complete,
  302. },
  303. [HANDLER_FW] = {
  304. .name = "load-firmware",
  305. .handler = handle_firmware,
  306. },
  307. [HANDLER_START_CONSOLE] = {
  308. .name = "start-console",
  309. .handler = handle_start_console,
  310. },
  311. };
  312. static void queue_next(void)
  313. {
  314. struct cmd_queue *c;
  315. if (queue_proc.pending || list_empty(&cmd_queue))
  316. return;
  317. c = list_first_entry(&cmd_queue, struct cmd_queue, list);
  318. queue_proc.pid = fork();
  319. if (!queue_proc.pid) {
  320. uloop_done();
  321. c->handler(c->msg, c->data);
  322. exit(0);
  323. }
  324. if (c->start)
  325. c->start(c->msg, c->data);
  326. list_del(&c->list);
  327. if (c->complete)
  328. current = c;
  329. else
  330. free(c);
  331. if (queue_proc.pid <= 0) {
  332. queue_next();
  333. return;
  334. }
  335. uloop_process_add(&queue_proc);
  336. DEBUG(4, "Launched hotplug exec instance, pid=%d\n", (int) queue_proc.pid);
  337. }
  338. static void queue_proc_cb(struct uloop_process *c, int ret)
  339. {
  340. DEBUG(4, "Finished hotplug exec instance, pid=%d\n", (int) c->pid);
  341. if (current) {
  342. current->complete(current->msg, current->data, ret);
  343. free(current);
  344. current = NULL;
  345. }
  346. queue_next();
  347. }
  348. static void queue_add(struct cmd_handler *h, struct blob_attr *msg, struct blob_attr *data)
  349. {
  350. struct cmd_queue *c = NULL;
  351. struct blob_attr *_msg, *_data;
  352. c = calloc_a(sizeof(struct cmd_queue),
  353. &_msg, blob_pad_len(msg),
  354. &_data, blob_pad_len(data),
  355. NULL);
  356. if (!c)
  357. return;
  358. c->msg = _msg;
  359. c->data = _data;
  360. memcpy(c->msg, msg, blob_pad_len(msg));
  361. memcpy(c->data, data, blob_pad_len(data));
  362. c->handler = h->handler;
  363. c->complete = h->complete;
  364. c->start = h->start;
  365. list_add_tail(&c->list, &cmd_queue);
  366. queue_next();
  367. }
  368. static void handle_button_timeout(struct uloop_timeout *t)
  369. {
  370. struct button_timeout *b;
  371. char seen[16];
  372. b = container_of(t, struct button_timeout, timeout);
  373. blob_buf_init(&button_buf, 0);
  374. blobmsg_add_string(&button_buf, "BUTTON", b->name);
  375. blobmsg_add_string(&button_buf, "ACTION", "timeout");
  376. snprintf(seen, sizeof(seen), "%d", b->seen);
  377. blobmsg_add_string(&button_buf, "SEEN", seen);
  378. queue_add(&handlers[HANDLER_EXEC], button_buf.head, b->data);
  379. button_free(b);
  380. }
  381. static void handle_button_complete(struct blob_attr *msg, struct blob_attr *data, int ret)
  382. {
  383. char *name = hotplug_msg_find_var(msg, "BUTTON");
  384. struct button_timeout *b;
  385. int timeout = ret >> 8;
  386. if (!timeout)
  387. return;
  388. if (!name)
  389. return;
  390. b = calloc(1, sizeof(*b));
  391. if (!b)
  392. return;
  393. b->data = malloc(blob_pad_len(data));
  394. b->name = strdup(name);
  395. b->seen = timeout;
  396. memcpy(b->data, data, blob_pad_len(data));
  397. b->timeout.cb = handle_button_timeout;
  398. uloop_timeout_set(&b->timeout, timeout * 1000);
  399. list_add(&b->list, &button_timer);
  400. }
  401. static const char* rule_handle_var(struct json_script_ctx *ctx, const char *name, struct blob_attr *vars)
  402. {
  403. const char *str, *sep;
  404. if (!strcmp(name, "DEVICENAME") || !strcmp(name, "DEVNAME")) {
  405. str = json_script_find_var(ctx, vars, "DEVPATH");
  406. if (!str)
  407. return NULL;
  408. sep = strrchr(str, '/');
  409. if (sep)
  410. return sep + 1;
  411. return str;
  412. }
  413. return NULL;
  414. }
  415. static struct json_script_file *
  416. rule_handle_file(struct json_script_ctx *ctx, const char *name)
  417. {
  418. json_object *obj;
  419. obj = json_object_from_file((char*)name);
  420. if (!obj)
  421. return NULL;
  422. blob_buf_init(&script, 0);
  423. blobmsg_add_json_element(&script, "", obj);
  424. return json_script_file_from_blobmsg(name, blob_data(script.head), blob_len(script.head));
  425. }
  426. static void rule_handle_command(struct json_script_ctx *ctx, const char *name,
  427. struct blob_attr *data, struct blob_attr *vars)
  428. {
  429. struct blob_attr *cur;
  430. int rem, i;
  431. if (debug > 3) {
  432. DEBUG(4, "Command: %s\n", name);
  433. blobmsg_for_each_attr(cur, data, rem)
  434. DEBUG(4, " %s\n", (char *) blobmsg_data(cur));
  435. DEBUG(4, "Message:\n");
  436. blobmsg_for_each_attr(cur, vars, rem)
  437. DEBUG(4, " %s=%s\n", blobmsg_name(cur), (char *) blobmsg_data(cur));
  438. }
  439. for (i = 0; i < ARRAY_SIZE(handlers); i++)
  440. if (!strcmp(handlers[i].name, name)) {
  441. if (handlers[i].atomic)
  442. handlers[i].handler(vars, data);
  443. else
  444. queue_add(&handlers[i], vars, data);
  445. break;
  446. }
  447. if (last_event.cb)
  448. uloop_timeout_set(&last_event, HOTPLUG_WAIT);
  449. }
  450. static void rule_handle_error(struct json_script_ctx *ctx, const char *msg,
  451. struct blob_attr *context)
  452. {
  453. char *s;
  454. s = blobmsg_format_json(context, false);
  455. ERROR("ERROR: %s in block: %s\n", msg, s);
  456. free(s);
  457. }
  458. static struct json_script_ctx jctx = {
  459. .handle_var = rule_handle_var,
  460. .handle_error = rule_handle_error,
  461. .handle_command = rule_handle_command,
  462. .handle_file = rule_handle_file,
  463. };
  464. static void hotplug_handler_debug(struct blob_attr *data)
  465. {
  466. char *str;
  467. if (debug < 3)
  468. return;
  469. str = blobmsg_format_json(data, true);
  470. DEBUG(3, "%s\n", str);
  471. free(str);
  472. }
  473. static void hotplug_handler(struct uloop_fd *u, unsigned int ev)
  474. {
  475. int i = 0;
  476. static char buf[4096];
  477. int len = recv(u->fd, buf, sizeof(buf) - 1, MSG_DONTWAIT);
  478. void *index;
  479. if (len < 1)
  480. return;
  481. buf[len] = '\0';
  482. blob_buf_init(&b, 0);
  483. index = blobmsg_open_table(&b, NULL);
  484. while (i < len) {
  485. int l = strlen(buf + i) + 1;
  486. char *e = strstr(&buf[i], "=");
  487. if (e) {
  488. *e = '\0';
  489. blobmsg_add_string(&b, &buf[i], &e[1]);
  490. }
  491. i += l;
  492. }
  493. blobmsg_close_table(&b, index);
  494. hotplug_handler_debug(b.head);
  495. json_script_run(&jctx, rule_file, blob_data(b.head));
  496. }
  497. static struct uloop_fd hotplug_fd = {
  498. .cb = hotplug_handler,
  499. };
  500. void hotplug_last_event(uloop_timeout_handler handler)
  501. {
  502. last_event.cb = handler;
  503. if (handler)
  504. uloop_timeout_set(&last_event, HOTPLUG_WAIT);
  505. else
  506. uloop_timeout_cancel(&last_event);
  507. }
  508. void hotplug(char *rules)
  509. {
  510. struct sockaddr_nl nls = {};
  511. int nlbufsize = 512 * 1024;
  512. rule_file = strdup(rules);
  513. nls.nl_family = AF_NETLINK;
  514. nls.nl_pid = getpid();
  515. nls.nl_groups = -1;
  516. if ((hotplug_fd.fd = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_KOBJECT_UEVENT)) == -1) {
  517. ERROR("Failed to open hotplug socket: %m\n");
  518. exit(1);
  519. }
  520. if (bind(hotplug_fd.fd, (void *)&nls, sizeof(struct sockaddr_nl))) {
  521. ERROR("Failed to bind hotplug socket: %m\n");
  522. exit(1);
  523. }
  524. if (setsockopt(hotplug_fd.fd, SOL_SOCKET, SO_RCVBUFFORCE, &nlbufsize, sizeof(nlbufsize)))
  525. ERROR("Failed to resize receive buffer: %m\n");
  526. json_script_init(&jctx);
  527. queue_proc.cb = queue_proc_cb;
  528. uloop_fd_add(&hotplug_fd, ULOOP_READ);
  529. }
  530. int hotplug_run(char *rules)
  531. {
  532. uloop_init();
  533. hotplug(rules);
  534. uloop_run();
  535. return 0;
  536. }
  537. void hotplug_shutdown(void)
  538. {
  539. uloop_fd_delete(&hotplug_fd);
  540. close(hotplug_fd.fd);
  541. }