PageRenderTime 66ms CodeModel.GetById 33ms RepoModel.GetById 0ms app.codeStats 1ms

/open-iscsi-2.0-872-rc4-bnx2i/usr/mgmt_ipc.c

#
C | 560 lines | 419 code | 84 blank | 57 comment | 56 complexity | f750f41c22ae1840947147c60861d3a3 MD5 | raw file
Possible License(s): LGPL-2.1, GPL-2.0, GPL-3.0
  1. /*
  2. * iSCSI Administrator Utility Socket Interface
  3. *
  4. * Copyright (C) 2004 Dmitry Yusupov, Alex Aizman
  5. * Copyright (C) 2006 Mike Christie
  6. * Copyright (C) 2006 Red Hat, Inc. All rights reserved.
  7. * maintained by open-iscsi@googlegroups.com
  8. *
  9. * Originally based on:
  10. * (C) 2004 FUJITA Tomonori <tomof@acm.org>
  11. *
  12. * This program is free software; you can redistribute it and/or modify
  13. * it under the terms of the GNU General Public License as published
  14. * by the Free Software Foundation; either version 2 of the License, or
  15. * (at your option) any later version.
  16. *
  17. * This program is distributed in the hope that it will be useful, but
  18. * WITHOUT ANY WARRANTY; without even the implied warranty of
  19. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  20. * General Public License for more details.
  21. *
  22. * See the file COPYING included with this distribution for more details.
  23. */
  24. #include <stdlib.h>
  25. #include <errno.h>
  26. #include <unistd.h>
  27. #include <pwd.h>
  28. #include <sys/un.h>
  29. #include "iscsid.h"
  30. #include "idbm.h"
  31. #include "mgmt_ipc.h"
  32. #include "event_poll.h"
  33. #include "log.h"
  34. #include "transport.h"
  35. #include "sysdeps.h"
  36. #include "iscsi_ipc.h"
  37. #include "iscsi_err.h"
  38. #define PEERUSER_MAX 64
  39. #define EXTMSG_MAX (64 * 1024)
  40. int
  41. mgmt_ipc_listen(void)
  42. {
  43. int fd, err;
  44. struct sockaddr_un addr;
  45. fd = socket(AF_LOCAL, SOCK_STREAM, 0);
  46. if (fd < 0) {
  47. log_error("Can not create IPC socket");
  48. return fd;
  49. }
  50. memset(&addr, 0, sizeof(addr));
  51. addr.sun_family = AF_LOCAL;
  52. memcpy((char *) &addr.sun_path + 1, ISCSIADM_NAMESPACE,
  53. strlen(ISCSIADM_NAMESPACE));
  54. if ((err = bind(fd, (struct sockaddr *) &addr, sizeof(addr))) < 0) {
  55. log_error("Can not bind IPC socket");
  56. close(fd);
  57. return err;
  58. }
  59. if ((err = listen(fd, 32)) < 0) {
  60. log_error("Can not listen IPC socket");
  61. close(fd);
  62. return err;
  63. }
  64. return fd;
  65. }
  66. void
  67. mgmt_ipc_close(int fd)
  68. {
  69. event_loop_exit(NULL);
  70. if (fd >= 0)
  71. close(fd);
  72. }
  73. static int
  74. mgmt_ipc_session_login(queue_task_t *qtask)
  75. {
  76. return session_login_task(&qtask->req.u.session.rec, qtask);
  77. }
  78. static int
  79. mgmt_ipc_session_getstats(queue_task_t *qtask)
  80. {
  81. int sid = qtask->req.u.session.sid;
  82. iscsi_session_t *session;
  83. int rc;
  84. if (!(session = session_find_by_sid(sid)))
  85. return ISCSI_ERR_SESS_NOT_FOUND;
  86. rc = ipc->get_stats(session->t->handle,
  87. session->id, session->conn[0].id,
  88. (void *)&qtask->rsp.u.getstats,
  89. MGMT_IPC_GETSTATS_BUF_MAX);
  90. if (rc) {
  91. log_error("get_stats(): IPC error %d "
  92. "session [%02d]", rc, sid);
  93. return ISCSI_ERR_INTERNAL;
  94. }
  95. mgmt_ipc_write_rsp(qtask, ISCSI_SUCCESS);
  96. return ISCSI_SUCCESS;
  97. }
  98. static int
  99. mgmt_ipc_send_targets(queue_task_t *qtask)
  100. {
  101. iscsiadm_req_t *req = &qtask->req;
  102. int err;
  103. err = iscsi_host_send_targets(qtask, req->u.st.host_no,
  104. req->u.st.do_login,
  105. &req->u.st.ss);
  106. mgmt_ipc_write_rsp(qtask, err);
  107. return ISCSI_SUCCESS;
  108. }
  109. static int
  110. mgmt_ipc_session_logout(queue_task_t *qtask)
  111. {
  112. return session_logout_task(qtask->req.u.session.sid, qtask);
  113. }
  114. static int
  115. mgmt_ipc_session_sync(queue_task_t *qtask)
  116. {
  117. struct ipc_msg_session *session= &qtask->req.u.session;
  118. return iscsi_sync_session(&session->rec, qtask, session->sid);
  119. }
  120. static int
  121. mgmt_ipc_cfg_initiatorname(queue_task_t *qtask)
  122. {
  123. if (dconfig->initiator_name)
  124. strcpy(qtask->rsp.u.config.var, dconfig->initiator_name);
  125. mgmt_ipc_write_rsp(qtask, ISCSI_SUCCESS);
  126. return ISCSI_SUCCESS;
  127. }
  128. static int
  129. mgmt_ipc_session_info(queue_task_t *qtask)
  130. {
  131. int sid = qtask->req.u.session.sid;
  132. iscsi_session_t *session;
  133. struct ipc_msg_session_state *info;
  134. if (!(session = session_find_by_sid(sid))) {
  135. log_debug(1, "session with sid %d not found!", sid);
  136. return ISCSI_ERR_SESS_NOT_FOUND;
  137. }
  138. info = &qtask->rsp.u.session_state;
  139. info->conn_state = session->conn[0].state;
  140. info->session_state = session->r_stage;
  141. mgmt_ipc_write_rsp(qtask, ISCSI_SUCCESS);
  142. return ISCSI_SUCCESS;
  143. }
  144. static int
  145. mgmt_ipc_cfg_initiatoralias(queue_task_t *qtask)
  146. {
  147. strcpy(qtask->rsp.u.config.var, dconfig->initiator_alias);
  148. mgmt_ipc_write_rsp(qtask, ISCSI_SUCCESS);
  149. return ISCSI_SUCCESS;
  150. }
  151. static int
  152. mgmt_ipc_cfg_filename(queue_task_t *qtask)
  153. {
  154. strcpy(qtask->rsp.u.config.var, dconfig->config_file);
  155. mgmt_ipc_write_rsp(qtask, ISCSI_SUCCESS);
  156. return ISCSI_SUCCESS;
  157. }
  158. static int
  159. mgmt_ipc_conn_add(queue_task_t *qtask)
  160. {
  161. return ISCSI_ERR;
  162. }
  163. static int
  164. mgmt_ipc_immediate_stop(queue_task_t *qtask)
  165. {
  166. event_loop_exit(qtask);
  167. return ISCSI_SUCCESS;
  168. }
  169. static int
  170. mgmt_ipc_conn_remove(queue_task_t *qtask)
  171. {
  172. return ISCSI_ERR;
  173. }
  174. /*
  175. * Parse a list of strings, encoded as a 32bit
  176. * length followed by the string itself (not necessarily
  177. * NUL-terminated).
  178. */
  179. static int
  180. mgmt_ipc_parse_strings(queue_task_t *qtask, char ***result)
  181. {
  182. char *data, *endp, **argv = NULL;
  183. unsigned int left, argc;
  184. again:
  185. data = qtask->payload;
  186. left = qtask->req.payload_len;
  187. endp = NULL;
  188. argc = 0;
  189. while (left) {
  190. uint32_t len;
  191. if (left < 4)
  192. return -1;
  193. memcpy(&len, data, 4);
  194. data += 4;
  195. if (endp)
  196. *endp = '\0';
  197. if (len > left)
  198. return -1;
  199. if (argv) {
  200. argv[argc] = (char *) data;
  201. endp = data + len;
  202. }
  203. data += len;
  204. argc++;
  205. }
  206. if (endp)
  207. *endp = '\0';
  208. if (argv == NULL) {
  209. argv = malloc((argc + 1) * sizeof(char *));
  210. *result = argv;
  211. goto again;
  212. }
  213. argv[argc] = NULL;
  214. return argc;
  215. }
  216. static int
  217. mgmt_ipc_notify_common(queue_task_t *qtask, int (*handler)(int, char **))
  218. {
  219. char **argv = NULL;
  220. int argc, err = ISCSI_ERR;
  221. argc = mgmt_ipc_parse_strings(qtask, &argv);
  222. if (argc > 0)
  223. err = handler(argc, argv);
  224. if (argv)
  225. free(argv);
  226. mgmt_ipc_write_rsp(qtask, err);
  227. return ISCSI_SUCCESS;
  228. }
  229. /* Replace these dummies as you implement them
  230. elsewhere */
  231. static int
  232. iscsi_discovery_add_node(int argc, char **argv)
  233. {
  234. return ISCSI_SUCCESS;
  235. }
  236. static int
  237. iscsi_discovery_del_node(int argc, char **argv)
  238. {
  239. return ISCSI_SUCCESS;
  240. }
  241. static int
  242. iscsi_discovery_add_portal(int argc, char **argv)
  243. {
  244. return ISCSI_SUCCESS;
  245. }
  246. static int
  247. iscsi_discovery_del_portal(int argc, char **argv)
  248. {
  249. return ISCSI_SUCCESS;
  250. }
  251. static int
  252. mgmt_ipc_notify_add_node(queue_task_t *qtask)
  253. {
  254. return mgmt_ipc_notify_common(qtask, iscsi_discovery_add_node);
  255. }
  256. static int
  257. mgmt_ipc_notify_del_node(queue_task_t *qtask)
  258. {
  259. return mgmt_ipc_notify_common(qtask, iscsi_discovery_del_node);
  260. }
  261. static int
  262. mgmt_ipc_notify_add_portal(queue_task_t *qtask)
  263. {
  264. return mgmt_ipc_notify_common(qtask, iscsi_discovery_add_portal);
  265. }
  266. static int
  267. mgmt_ipc_notify_del_portal(queue_task_t *qtask)
  268. {
  269. return mgmt_ipc_notify_common(qtask, iscsi_discovery_del_portal);
  270. }
  271. static int
  272. mgmt_peeruser(int sock, char *user)
  273. {
  274. #if defined(SO_PEERCRED)
  275. /* Linux style: use getsockopt(SO_PEERCRED) */
  276. struct ucred peercred;
  277. socklen_t so_len = sizeof(peercred);
  278. struct passwd *pass;
  279. errno = 0;
  280. if (getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &peercred,
  281. &so_len) != 0 || so_len != sizeof(peercred)) {
  282. /* We didn't get a valid credentials struct. */
  283. log_error("peeruser_unux: error receiving credentials: %m");
  284. return 0;
  285. }
  286. pass = getpwuid(peercred.uid);
  287. if (pass == NULL) {
  288. log_error("peeruser_unix: unknown local user with uid %d",
  289. (int) peercred.uid);
  290. return 0;
  291. }
  292. strlcpy(user, pass->pw_name, PEERUSER_MAX);
  293. return 1;
  294. #elif defined(SCM_CREDS)
  295. struct msghdr msg;
  296. typedef struct cmsgcred Cred;
  297. #define cruid cmcred_uid
  298. Cred *cred;
  299. /* Compute size without padding */
  300. /* for NetBSD */
  301. char cmsgmem[_ALIGN(sizeof(struct cmsghdr)) + _ALIGN(sizeof(Cred))];
  302. /* Point to start of first structure */
  303. struct cmsghdr *cmsg = (struct cmsghdr *) cmsgmem;
  304. struct iovec iov;
  305. char buf;
  306. struct passwd *pw;
  307. memset(&msg, 0, sizeof(msg));
  308. msg.msg_iov = &iov;
  309. msg.msg_iovlen = 1;
  310. msg.msg_control = (char *) cmsg;
  311. msg.msg_controllen = sizeof(cmsgmem);
  312. memset(cmsg, 0, sizeof(cmsgmem));
  313. /*
  314. * The one character which is received here is not meaningful; its
  315. * purposes is only to make sure that recvmsg() blocks long enough for
  316. * the other side to send its credentials.
  317. */
  318. iov.iov_base = &buf;
  319. iov.iov_len = 1;
  320. if (recvmsg(sock, &msg, 0) < 0 || cmsg->cmsg_len < sizeof(cmsgmem) ||
  321. cmsg->cmsg_type != SCM_CREDS) {
  322. log_error("ident_unix: error receiving credentials: %m");
  323. return 0;
  324. }
  325. cred = (Cred *) CMSG_DATA(cmsg);
  326. pw = getpwuid(cred->cruid);
  327. if (pw == NULL) {
  328. log_error("ident_unix: unknown local user with uid %d",
  329. (int) cred->cruid);
  330. return 0;
  331. }
  332. strlcpy(user, pw->pw_name, PEERUSER_MAX);
  333. return 1;
  334. #else
  335. log_error("'mgmg_ipc' auth is not supported on local connections "
  336. "on this platform");
  337. return 0;
  338. #endif
  339. }
  340. static void
  341. mgmt_ipc_destroy_queue_task(queue_task_t *qtask)
  342. {
  343. if (qtask->mgmt_ipc_fd >= 0)
  344. close(qtask->mgmt_ipc_fd);
  345. if (qtask->payload)
  346. free(qtask->payload);
  347. if (qtask->allocated)
  348. free(qtask);
  349. }
  350. /*
  351. * Send the IPC response and destroy the queue_task.
  352. * The recovery code uses a qtask which is allocated as
  353. * part of a larger structure, and we don't want it to
  354. * get freed when we come here. This is what qtask->allocated
  355. * is for.
  356. */
  357. void
  358. mgmt_ipc_write_rsp(queue_task_t *qtask, int err)
  359. {
  360. if (!qtask)
  361. return;
  362. log_debug(4, "%s: rsp to fd %d", __FUNCTION__,
  363. qtask->mgmt_ipc_fd);
  364. if (qtask->mgmt_ipc_fd < 0) {
  365. mgmt_ipc_destroy_queue_task(qtask);
  366. return;
  367. }
  368. qtask->rsp.err = err;
  369. if (write(qtask->mgmt_ipc_fd, &qtask->rsp, sizeof(qtask->rsp)) < 0)
  370. log_error("IPC qtask write failed: %s", strerror(errno));
  371. close(qtask->mgmt_ipc_fd);
  372. mgmt_ipc_destroy_queue_task(qtask);
  373. }
  374. static int
  375. mgmt_ipc_read_data(int fd, void *ptr, size_t len)
  376. {
  377. int n;
  378. while (len) {
  379. n = read(fd, ptr, len);
  380. if (n < 0) {
  381. if (errno == EINTR)
  382. continue;
  383. return -EIO;
  384. }
  385. if (n == 0) {
  386. /* Client closed connection */
  387. return -EIO;
  388. }
  389. ptr += n;
  390. len -= n;
  391. }
  392. return 0;
  393. }
  394. static int
  395. mgmt_ipc_read_req(queue_task_t *qtask)
  396. {
  397. iscsiadm_req_t *req = &qtask->req;
  398. int rc;
  399. rc = mgmt_ipc_read_data(qtask->mgmt_ipc_fd, req, sizeof(*req));
  400. if (rc >= 0 && req->payload_len > 0) {
  401. /* Limit what we accept */
  402. if (req->payload_len > EXTMSG_MAX)
  403. return -EIO;
  404. /* Remember the allocated pointer in the
  405. * qtask - it will be freed by write_rsp.
  406. * Note: we allocate one byte in excess
  407. * so we can append a NUL byte. */
  408. qtask->payload = malloc(req->payload_len + 1);
  409. rc = mgmt_ipc_read_data(qtask->mgmt_ipc_fd,
  410. qtask->payload,
  411. req->payload_len);
  412. }
  413. return rc;
  414. }
  415. static mgmt_ipc_fn_t * mgmt_ipc_functions[__MGMT_IPC_MAX_COMMAND] = {
  416. [MGMT_IPC_SESSION_LOGIN] = mgmt_ipc_session_login,
  417. [MGMT_IPC_SESSION_LOGOUT] = mgmt_ipc_session_logout,
  418. [MGMT_IPC_SESSION_SYNC] = mgmt_ipc_session_sync,
  419. [MGMT_IPC_SESSION_STATS] = mgmt_ipc_session_getstats,
  420. [MGMT_IPC_SEND_TARGETS] = mgmt_ipc_send_targets,
  421. [MGMT_IPC_SESSION_INFO] = mgmt_ipc_session_info,
  422. [MGMT_IPC_CONN_ADD] = mgmt_ipc_conn_add,
  423. [MGMT_IPC_CONN_REMOVE] = mgmt_ipc_conn_remove,
  424. [MGMT_IPC_CONFIG_INAME] = mgmt_ipc_cfg_initiatorname,
  425. [MGMT_IPC_CONFIG_IALIAS] = mgmt_ipc_cfg_initiatoralias,
  426. [MGMT_IPC_CONFIG_FILE] = mgmt_ipc_cfg_filename,
  427. [MGMT_IPC_IMMEDIATE_STOP] = mgmt_ipc_immediate_stop,
  428. [MGMT_IPC_NOTIFY_ADD_NODE] = mgmt_ipc_notify_add_node,
  429. [MGMT_IPC_NOTIFY_DEL_NODE] = mgmt_ipc_notify_del_node,
  430. [MGMT_IPC_NOTIFY_ADD_PORTAL] = mgmt_ipc_notify_add_portal,
  431. [MGMT_IPC_NOTIFY_DEL_PORTAL] = mgmt_ipc_notify_del_portal,
  432. };
  433. void mgmt_ipc_handle(int accept_fd)
  434. {
  435. unsigned int command;
  436. int fd, err;
  437. queue_task_t *qtask = NULL;
  438. mgmt_ipc_fn_t *handler = NULL;
  439. char user[PEERUSER_MAX];
  440. qtask = calloc(1, sizeof(queue_task_t));
  441. if (!qtask)
  442. return;
  443. if ((fd = accept(accept_fd, NULL, NULL)) < 0) {
  444. free(qtask);
  445. return;
  446. }
  447. qtask->allocated = 1;
  448. qtask->mgmt_ipc_fd = fd;
  449. if (!mgmt_peeruser(fd, user) || strncmp(user, "root", PEERUSER_MAX)) {
  450. err = ISCSI_ERR_ACCESS;
  451. goto err;
  452. }
  453. if (mgmt_ipc_read_req(qtask) < 0) {
  454. mgmt_ipc_destroy_queue_task(qtask);
  455. return;
  456. }
  457. command = qtask->req.command;
  458. qtask->rsp.command = command;
  459. if (0 <= command && command < __MGMT_IPC_MAX_COMMAND)
  460. handler = mgmt_ipc_functions[command];
  461. if (handler != NULL) {
  462. /* If the handler returns OK, this means it
  463. * already sent the reply. */
  464. err = handler(qtask);
  465. if (err == ISCSI_SUCCESS)
  466. return;
  467. } else {
  468. log_error("unknown request: %s(%d) %u",
  469. __FUNCTION__, __LINE__, command);
  470. err = ISCSI_ERR_INVALID_MGMT_REQ;
  471. }
  472. err:
  473. /* This will send the response, close the
  474. * connection and free the qtask */
  475. mgmt_ipc_write_rsp(qtask, err);
  476. }