/* * iscsid communication helpers * * Copyright (C) 2004 Dmitry Yusupov, Alex Aizman * Copyright (C) 2006 - 2010 Mike Christie * Copyright (C) 2006 - 2010 Red Hat, Inc. All rights reserved. * maintained by open-iscsi@googlegroups.com * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published * by the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * See the file COPYING included with this distribution for more details. */ #include #include #include #include #include #include #include #include #include #include #include "initiator.h" #include "log.h" #include "mgmt_ipc.h" #include "iscsi_util.h" #include "config.h" #include "iscsi_err.h" #include "iscsid_req.h" #include "uip_mgmt_ipc.h" static void iscsid_startup(void) { char *startup_cmd; startup_cmd = cfg_get_string_param(CONFIG_FILE, "iscsid.startup"); if (!startup_cmd) { log_error("iscsid is not running. Could not start it up " "automatically using the startup command in the " "/etc/iscsi/iscsid.conf iscsid.startup setting. " "Please check that the file exists or that your " "init scripts have started iscsid."); return; } if (system(startup_cmd) < 0) log_error("Could not execute '%s' (err %d)", startup_cmd, errno); free(startup_cmd); } #define MAXSLEEP 128 static int ipc_connect(int *fd, char *unix_sock_name, int start_iscsid) { int nsec, addr_len; struct sockaddr_un addr; *fd = socket(AF_LOCAL, SOCK_STREAM, 0); if (*fd < 0) { log_error("can not create IPC socket (%d)!", errno); return ISCSI_ERR_ISCSID_NOTCONN; } addr_len = setup_abstract_addr(&addr, unix_sock_name); /* * Trying to connect with exponential backoff */ for (nsec = 1; nsec <= MAXSLEEP; nsec <<= 1) { if (connect(*fd, (struct sockaddr *) &addr, addr_len) == 0) /* Connection established */ return ISCSI_SUCCESS; /* If iscsid isn't there, there's no sense * in retrying. */ if (errno == ECONNREFUSED) { if (start_iscsid && nsec == 1) iscsid_startup(); else break; } /* * Delay before trying again */ if (nsec <= MAXSLEEP/2) sleep(nsec); } close(*fd); *fd = -1; log_error("can not connect to iSCSI daemon (%d)!", errno); return ISCSI_ERR_ISCSID_NOTCONN; } char iscsid_namespace[64] = ISCSIADM_NAMESPACE; void iscsid_set_namespace(pid_t pid) { if (pid) { snprintf(iscsid_namespace, 64, ISCSIADM_NAMESPACE "-%d", pid); } else { snprintf(iscsid_namespace, 64, ISCSIADM_NAMESPACE); } } static int iscsid_connect(int *fd, int start_iscsid) { return ipc_connect(fd, iscsid_namespace, start_iscsid); } int iscsid_request(int *fd, iscsiadm_req_t *req, int start_iscsid) { int err; err = iscsid_connect(fd, start_iscsid); if (err) return err; if ((err = write(*fd, req, sizeof(*req))) != sizeof(*req)) { log_error("got write error (%d/%d) on cmd %d, daemon died?", err, errno, req->command); close(*fd); return ISCSI_ERR_ISCSID_COMM_ERR; } return ISCSI_SUCCESS; } int iscsid_response(int fd, iscsiadm_cmd_e cmd, iscsiadm_rsp_t *rsp, int timeout) { size_t len = sizeof(*rsp); int iscsi_err = ISCSI_ERR_ISCSID_COMM_ERR; int err; int poll_wait = 0; if (timeout == -1) { timeout = ISCSID_REQ_TIMEOUT; poll_wait = 1; } while (len) { struct pollfd pfd; pfd.fd = fd; pfd.events = POLLIN; err = poll(&pfd, 1, timeout); if (!err) { if (poll_wait) continue; return ISCSI_ERR_ISCSID_NOTCONN; } else if (err < 0) { if (errno == EINTR) continue; log_error("got poll error (%d/%d), daemon died?", err, errno); return ISCSI_ERR_ISCSID_COMM_ERR; } else if (pfd.revents & POLLIN) { err = recv(fd, rsp, sizeof(*rsp), MSG_WAITALL); if (err < 0) { log_error("read error (%d/%d), daemon died?", err, errno); break; } len -= err; iscsi_err = rsp->err; } } close(fd); if (!iscsi_err && cmd != rsp->command) iscsi_err = ISCSI_ERR_ISCSID_COMM_ERR; return iscsi_err; } int iscsid_exec_req(iscsiadm_req_t *req, iscsiadm_rsp_t *rsp, int start_iscsid, int tmo) { int fd; int err; err = iscsid_request(&fd, req, start_iscsid); if (err) return err; return iscsid_response(fd, req->command, rsp, tmo); } int iscsid_req_wait(iscsiadm_cmd_e cmd, int fd) { iscsiadm_rsp_t rsp; memset(&rsp, 0, sizeof(iscsiadm_rsp_t)); return iscsid_response(fd, cmd, &rsp, -1); } int iscsid_req_by_rec_async(iscsiadm_cmd_e cmd, node_rec_t *rec, int *fd) { iscsiadm_req_t req; memset(&req, 0, sizeof(iscsiadm_req_t)); req.command = cmd; memcpy(&req.u.session.rec, rec, sizeof(node_rec_t)); return iscsid_request(fd, &req, 1); } int iscsid_req_by_rec(iscsiadm_cmd_e cmd, node_rec_t *rec) { int err, fd; err = iscsid_req_by_rec_async(cmd, rec, &fd); if (err) return err; return iscsid_req_wait(cmd, fd); } int iscsid_req_by_sid_async(iscsiadm_cmd_e cmd, int sid, int *fd) { iscsiadm_req_t req; memset(&req, 0, sizeof(iscsiadm_req_t)); req.command = cmd; req.u.session.sid = sid; return iscsid_request(fd, &req, 1); } int iscsid_req_by_sid(iscsiadm_cmd_e cmd, int sid) { int err, fd; err = iscsid_req_by_sid_async(cmd, sid, &fd); if (err) return err; return iscsid_req_wait(cmd, fd); } static int uip_connect(int *fd) { return ipc_connect(fd, ISCSID_UIP_NAMESPACE, 0); } int uip_broadcast(void *buf, size_t buf_len, int fd_flags, uint32_t *status) { int err; int fd; iscsid_uip_rsp_t rsp; int flags; int count; size_t write_res; err = uip_connect(&fd); if (err) { log_warning("uIP daemon is not up"); return err; } log_debug(3, "connected to uIP daemon"); /* Send the data to uIP */ write_res = write(fd, buf, buf_len); if (write_res != buf_len) { log_error("got write error (%d/%d), daemon died?", (int)write_res, errno); close(fd); return ISCSI_ERR_ISCSID_COMM_ERR; } log_debug(3, "send iface config to uIP daemon"); /* Set the socket to a non-blocking read, this way if there are * problems waiting for uIP, iscsid can bailout early */ flags = fcntl(fd, F_GETFL, 0); if (flags == -1) flags = 0; if (fd_flags) flags |= fd_flags; err = fcntl(fd, F_SETFL, flags); if (err) { log_error("could not set uip broadcast to non-blocking: %d", errno); close(fd); return ISCSI_ERR; } #define MAX_UIP_BROADCAST_READ_TRIES 5 for (count = 0; count < MAX_UIP_BROADCAST_READ_TRIES; count++) { /* Wait for the response */ err = read(fd, &rsp, sizeof(rsp)); if (err == sizeof(rsp)) { log_debug(3, "Broadcasted to uIP with length: %zu cmd: 0x%x rsp: 0x%x", buf_len, rsp.command, rsp.err); err = 0; break; } else if ((err == -1) && (errno == EAGAIN)) { usleep(1000000); continue; } else { log_error("Could not read response (%d/%d), daemon " "died?", err, errno); err = ISCSI_ERR; break; } } if (count == MAX_UIP_BROADCAST_READ_TRIES) { log_error("Could not broadcast to uIP after %d tries", count); err = ISCSI_ERR_AGAIN; } if (err) goto done; switch (rsp.command) { case ISCSID_UIP_IPC_GET_IFACE: if (rsp.err != ISCSID_UIP_MGMT_IPC_DEVICE_UP) { log_debug(3, "Device is not ready\n"); err = ISCSI_ERR_AGAIN; } break; case ISCSID_UIP_IPC_PING: *status = rsp.ping_sc; if (rsp.err == ISCSID_UIP_MGMT_IPC_DEVICE_INITIALIZING) { log_debug(3, "Device is not ready\n"); err = ISCSI_ERR_AGAIN; } else if (*status) { err = ISCSI_ERR; } break; default: err = ISCSI_ERR; } done: close(fd); return err; }