4 * Copyright (C) 2006 Mike Christie
5 * Copyright (C) 2006 Red Hat, Inc. All rights reserved.
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published
9 * by the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
25 #include <sys/types.h>
29 #include "initiator.h"
30 #include "transport.h"
33 #include "iscsi_sysfs.h"
35 #include "iscsi_settings.h"
37 #include "session_info.h"
39 #include "iscsi_err.h"
42 * TODO: remove the _DIR defines and search for subsys dirs like
45 #define ISCSI_TRANSPORT_DIR "/sys/class/iscsi_transport"
46 #define ISCSI_SESSION_DIR "/sys/class/iscsi_session"
47 #define ISCSI_HOST_DIR "/sys/class/iscsi_host"
49 #define ISCSI_SESSION_SUBSYS "iscsi_session"
50 #define ISCSI_CONN_SUBSYS "iscsi_connection"
51 #define ISCSI_HOST_SUBSYS "iscsi_host"
52 #define ISCSI_TRANSPORT_SUBSYS "iscsi_transport"
53 #define ISCSI_IFACE_SUBSYS "iscsi_iface"
54 #define SCSI_HOST_SUBSYS "scsi_host"
55 #define SCSI_SUBSYS "scsi"
57 #define ISCSI_SESSION_ID "session%d"
58 #define ISCSI_CONN_ID "connection%d:0"
59 #define ISCSI_HOST_ID "host%d"
62 * TODO: make this into a real API and check inputs better and add doc.
65 static int num_transports
;
66 LIST_HEAD(transports
);
68 void free_transports(void)
70 struct iscsi_transport
*t
, *tmp
;
72 list_for_each_entry_safe(t
, tmp
, &transports
, list
) {
78 static int trans_filter(const struct dirent
*dir
)
80 return strcmp(dir
->d_name
, ".") && strcmp(dir
->d_name
, "..");
83 static int read_transports(void)
85 struct dirent
**namelist
;
87 struct iscsi_transport
*t
;
89 log_debug(7, "in %s", __FUNCTION__
);
91 n
= scandir(ISCSI_TRANSPORT_DIR
, &namelist
, trans_filter
,
94 log_error("Could not scan %s.", ISCSI_TRANSPORT_DIR
);
98 for (i
= 0; i
< n
; i
++) {
101 list_for_each_entry(t
, &transports
, list
) {
102 if (!strcmp(t
->name
, namelist
[i
]->d_name
)) {
109 /* copy new transport */
110 t
= malloc(sizeof(*t
));
113 log_debug(7, "Adding new transport %s",
114 namelist
[i
]->d_name
);
116 INIT_LIST_HEAD(&t
->sessions
);
117 INIT_LIST_HEAD(&t
->list
);
118 strlcpy(t
->name
, namelist
[i
]->d_name
,
119 ISCSI_TRANSPORT_NAME_MAXLEN
);
120 if (set_transport_template(t
)) {
125 log_debug(7, "Updating transport %s",
126 namelist
[i
]->d_name
);
128 if (sysfs_get_uint64(t
->name
, ISCSI_TRANSPORT_SUBSYS
,
129 "handle", &t
->handle
)) {
130 if (list_empty(&t
->list
))
133 log_error("Could not update %s.\n",
138 if (sysfs_get_uint(t
->name
, ISCSI_TRANSPORT_SUBSYS
,
140 if (list_empty(&t
->list
))
143 log_error("Could not update %s.\n",
148 * tmp hack for qla4xx compat
150 if (!strcmp(t
->name
, "qla4xxx")) {
151 t
->caps
|= CAP_DATA_PATH_OFFLOAD
;
154 if (list_empty(&t
->list
))
155 list_add_tail(&t
->list
, &transports
);
158 for (i
= 0; i
< n
; i
++)
166 /* caller must check lengths */
167 void iscsi_sysfs_get_auth_conf(int sid
, struct iscsi_auth_config
*conf
)
171 memset(conf
, 0, sizeof(*conf
));
172 snprintf(id
, sizeof(id
), ISCSI_SESSION_ID
, sid
);
174 sysfs_get_str(id
, ISCSI_SESSION_SUBSYS
, "username", conf
->username
,
175 sizeof(conf
->username
));
176 sysfs_get_str(id
, ISCSI_SESSION_SUBSYS
, "username_in",
177 conf
->username_in
, sizeof(conf
->username_in
));
179 sysfs_get_str(id
, ISCSI_SESSION_SUBSYS
, "password",
180 (char *)conf
->password
, sizeof(conf
->password
));
181 if (strlen((char *)conf
->password
))
182 conf
->password_length
= strlen((char *)conf
->password
);
184 sysfs_get_str(id
, ISCSI_SESSION_SUBSYS
, "password_in",
185 (char *)conf
->password_in
, sizeof(conf
->password_in
));
186 if (strlen((char *)conf
->password_in
))
187 conf
->password_in_length
= strlen((char *)conf
->password_in
);
190 /* called must check for -1=invalid value */
191 void iscsi_sysfs_get_negotiated_conn_conf(int sid
,
192 struct iscsi_conn_operational_config
*conf
)
196 memset(conf
, 0, sizeof(*conf
));
197 snprintf(id
, sizeof(id
), ISCSI_CONN_ID
, sid
);
199 sysfs_get_int(id
, ISCSI_CONN_SUBSYS
, "data_digest", &conf
->DataDigest
);
200 sysfs_get_int(id
, ISCSI_CONN_SUBSYS
, "header_digest",
201 &conf
->HeaderDigest
);
202 sysfs_get_int(id
, ISCSI_CONN_SUBSYS
, "max_xmit_dlength",
203 &conf
->MaxXmitDataSegmentLength
);
204 sysfs_get_int(id
, ISCSI_CONN_SUBSYS
, "max_recv_dlength",
205 &conf
->MaxRecvDataSegmentLength
);
208 /* called must check for -1=invalid value */
209 void iscsi_sysfs_get_negotiated_session_conf(int sid
,
210 struct iscsi_session_operational_config
*conf
)
214 memset(conf
, 0, sizeof(*conf
));
215 snprintf(id
, sizeof(id
), ISCSI_SESSION_ID
, sid
);
217 sysfs_get_int(id
, ISCSI_SESSION_SUBSYS
, "data_pdu_in_order",
218 &conf
->DataPDUInOrder
);
219 sysfs_get_int(id
, ISCSI_SESSION_SUBSYS
, "data_seq_in_order",
220 &conf
->DataSequenceInOrder
);
221 sysfs_get_int(id
, ISCSI_SESSION_SUBSYS
, "erl", &conf
->ERL
);
222 sysfs_get_int(id
, ISCSI_SESSION_SUBSYS
, "first_burst_len",
223 &conf
->FirstBurstLength
);
224 sysfs_get_int(id
, ISCSI_SESSION_SUBSYS
, "max_burst_len",
225 &conf
->MaxBurstLength
);
226 sysfs_get_int(id
, ISCSI_SESSION_SUBSYS
, "immediate_data",
227 &conf
->ImmediateData
);
228 sysfs_get_int(id
, ISCSI_SESSION_SUBSYS
, "initial_r2t",
230 sysfs_get_int(id
, ISCSI_SESSION_SUBSYS
, "max_outstanding_r2t",
231 &conf
->MaxOutstandingR2T
);
234 uint32_t iscsi_sysfs_get_host_no_from_sid(uint32_t sid
, int *err
)
236 struct sysfs_device
*session_dev
, *host_dev
;
237 char devpath
[PATH_SIZE
];
241 snprintf(id
, sizeof(id
), "session%u", sid
);
242 if (!sysfs_lookup_devpath_by_subsys_id(devpath
, sizeof(devpath
),
243 ISCSI_SESSION_SUBSYS
, id
)) {
244 log_error("Could not lookup devpath for %s. Possible sysfs "
245 "incompatibility.\n", id
);
246 *err
= ISCSI_ERR_SYSFS_LOOKUP
;
250 session_dev
= sysfs_device_get(devpath
);
252 log_error("Could not get dev for %s. Possible sysfs "
253 "incompatibility.\n", id
);
254 *err
= ISCSI_ERR_SYSFS_LOOKUP
;
259 * 2.6.27 moved from scsi_host to scsi for the subsys when
260 * sysfs compat is not on.
262 host_dev
= sysfs_device_get_parent_with_subsystem(session_dev
,
265 struct sysfs_device
*dev_parent
;
267 dev_parent
= sysfs_device_get_parent(session_dev
);
268 while (dev_parent
!= NULL
) {
269 if (strncmp(dev_parent
->kernel
, "host", 4) == 0) {
270 host_dev
= dev_parent
;
273 dev_parent
= sysfs_device_get_parent(dev_parent
);
277 log_error("Could not get host dev for %s. Possible "
278 "sysfs incompatibility.\n", id
);
279 *err
= ISCSI_ERR_SYSFS_LOOKUP
;
284 return atol(host_dev
->kernel_number
);
287 /* TODO: merge and make macro */
288 static int __get_host_no_from_netdev(void *data
, struct host_info
*info
)
290 struct host_info
*ret_info
= data
;
292 if (!strcmp(ret_info
->iface
.netdev
, info
->iface
.netdev
)) {
293 ret_info
->host_no
= info
->host_no
;
299 static uint32_t get_host_no_from_netdev(char *netdev
, int *rc
)
301 uint32_t host_no
= -1;
302 struct host_info
*info
;
303 int nr_found
, local_rc
;
307 info
= calloc(1, sizeof(*info
));
309 *rc
= ISCSI_ERR_NOMEM
;
312 strcpy(info
->iface
.netdev
, netdev
);
314 local_rc
= iscsi_sysfs_for_each_host(info
, &nr_found
,
315 __get_host_no_from_netdev
);
317 host_no
= info
->host_no
;
319 *rc
= ISCSI_ERR_HOST_NOT_FOUND
;
324 static int __get_host_no_from_hwaddress(void *data
, struct host_info
*info
)
326 struct host_info
*ret_info
= data
;
328 if (!strcasecmp(ret_info
->iface
.hwaddress
, info
->iface
.hwaddress
)) {
329 ret_info
->host_no
= info
->host_no
;
335 uint32_t iscsi_sysfs_get_host_no_from_hwaddress(char *hwaddress
, int *rc
)
337 uint32_t host_no
= -1;
338 struct host_info
*info
;
339 int nr_found
, local_rc
;
343 info
= calloc(1, sizeof(*info
));
345 *rc
= ISCSI_ERR_NOMEM
;
348 strcpy(info
->iface
.hwaddress
, hwaddress
);
350 local_rc
= iscsi_sysfs_for_each_host(info
, &nr_found
,
351 __get_host_no_from_hwaddress
);
353 host_no
= info
->host_no
;
355 *rc
= ISCSI_ERR_HOST_NOT_FOUND
;
360 static int __get_host_no_from_ipaddress(void *data
, struct host_info
*info
)
362 struct host_info
*ret_info
= data
;
364 if (!strcmp(ret_info
->iface
.ipaddress
, info
->iface
.ipaddress
)) {
365 ret_info
->host_no
= info
->host_no
;
371 static uint32_t get_host_no_from_ipaddress(char *address
, int *rc
)
373 uint32_t host_no
= -1;
374 struct host_info
*info
;
380 info
= calloc(1, sizeof(*info
));
382 *rc
= ISCSI_ERR_NOMEM
;
385 strcpy(info
->iface
.ipaddress
, address
);
387 local_rc
= iscsi_sysfs_for_each_host(info
, &nr_found
,
388 __get_host_no_from_ipaddress
);
390 host_no
= info
->host_no
;
392 *rc
= ISCSI_ERR_HOST_NOT_FOUND
;
397 uint32_t iscsi_sysfs_get_host_no_from_hwinfo(struct iface_rec
*iface
, int *rc
)
400 uint32_t host_no
= -1;
402 if (strlen(iface
->hwaddress
) &&
403 strcasecmp(iface
->hwaddress
, DEFAULT_HWADDRESS
))
404 host_no
= iscsi_sysfs_get_host_no_from_hwaddress(
405 iface
->hwaddress
, &tmp_rc
);
406 else if (strlen(iface
->netdev
) &&
407 strcasecmp(iface
->netdev
, DEFAULT_NETDEV
))
408 host_no
= get_host_no_from_netdev(iface
->netdev
, &tmp_rc
);
409 else if (strlen(iface
->ipaddress
) &&
410 strcasecmp(iface
->ipaddress
, DEFAULT_IPADDRESS
))
411 host_no
= get_host_no_from_ipaddress(iface
->ipaddress
, &tmp_rc
);
413 tmp_rc
= ISCSI_ERR_INVAL
;
420 * Read in iface settings based on host and session values. If
421 * session is not passed in, then the ifacename will not be set. And
422 * if the session is not passed in then iname will only be set for
425 static int iscsi_sysfs_read_iface(struct iface_rec
*iface
, int host_no
,
426 char *session
, char *iface_kern_id
)
428 uint32_t tmp_host_no
, iface_num
;
429 char host_id
[NAME_SIZE
];
430 struct iscsi_transport
*t
;
433 t
= iscsi_sysfs_get_transport_by_hba(host_no
);
435 log_debug(7, "could not get transport name for host%d",
438 strcpy(iface
->transport_name
, t
->name
);
440 snprintf(host_id
, sizeof(host_id
), ISCSI_HOST_ID
, host_no
);
443 * If we cannot get the address we assume we are doing the old
444 * style and use default.
446 ret
= sysfs_get_str(host_id
, ISCSI_HOST_SUBSYS
, "hwaddress",
447 iface
->hwaddress
, sizeof(iface
->hwaddress
));
449 log_debug(7, "could not read hwaddress for host%d\n", host_no
);
452 ret
= sysfs_get_str(iface_kern_id
, ISCSI_IFACE_SUBSYS
,
454 iface
->ipaddress
, sizeof(iface
->ipaddress
));
456 /* if not found just print out default */
457 ret
= sysfs_get_str(host_id
, ISCSI_HOST_SUBSYS
, "ipaddress",
458 iface
->ipaddress
, sizeof(iface
->ipaddress
));
460 log_debug(7, "could not read local address for host%d\n",
463 /* if not found just print out default */
464 ret
= sysfs_get_str(host_id
, ISCSI_HOST_SUBSYS
, "netdev",
465 iface
->netdev
, sizeof(iface
->netdev
));
467 log_debug(7, "could not read netdev for host%d\n", host_no
);
470 * For drivers like qla4xxx we can only set the iname at the
471 * host level because we cannot create different initiator ports
472 * (cannot set isid either). The LLD also exports the iname at the
473 * hba level so apps can see it, but we no longer set the iname for
474 * each iscsid controlled host since bnx2i cxgbi can support multiple
475 * initiator names and of course software iscsi can support anything.
478 memset(iface
->iname
, 0, sizeof(iface
->iname
));
480 ret
= sysfs_get_str(session
, ISCSI_SESSION_SUBSYS
,
482 iface
->iname
, sizeof(iface
->iname
));
484 * qlaxxx will not set this at the session level so we
485 * always drop down for it.
489 * For older kernels/tools (2.6.26 and below and 2.0.870)
490 * we will not have a session level initiator name, so
496 ret
= sysfs_get_str(host_id
, ISCSI_HOST_SUBSYS
, "initiatorname",
497 iface
->iname
, sizeof(iface
->iname
));
500 * default iname is picked up later from
501 * initiatorname.iscsi if software/partial-offload.
503 * TODO: we should make it easier to get the
504 * global iname so we can just fill it in here.
506 log_debug(7, "Could not read initiatorname for "
507 "host%d\n", host_no
);
508 /* optional so do not return error */
513 * this is on the session, because we support multiple bindings
516 memset(iface
->name
, 0, sizeof(iface
->name
));
519 * this was added after 2.0.869 so we could be doing iscsi_tcp
520 * session binding, but there may not be a ifacename set
521 * if binding is not used.
523 ret
= sysfs_get_str(session
, ISCSI_SESSION_SUBSYS
, "ifacename",
524 iface
->name
, sizeof(iface
->name
));
526 log_debug(7, "could not read iface name for "
527 "session %s\n", session
);
529 * if the ifacename file is not there then we are
530 * using a older kernel and can try to find the
531 * binding by the net info which was used on these
534 if (iface_get_by_net_binding(iface
, iface
))
535 log_debug(7, "Could not find iface for session "
536 "bound to:" iface_fmt
"\n",
544 strlcpy(iface
->name
, iface_kern_id
, sizeof(iface
->name
));
546 if (!strncmp(iface_kern_id
, "ipv4", 4)) {
547 sysfs_get_str(iface_kern_id
, ISCSI_IFACE_SUBSYS
, "bootproto",
548 iface
->bootproto
, sizeof(iface
->bootproto
));
550 sysfs_get_str(iface_kern_id
, ISCSI_IFACE_SUBSYS
, "gateway",
551 iface
->gateway
, sizeof(iface
->gateway
));
553 sysfs_get_str(iface_kern_id
, ISCSI_IFACE_SUBSYS
, "subnet",
554 iface
->subnet_mask
, sizeof(iface
->subnet_mask
));
556 sysfs_get_str(iface_kern_id
, ISCSI_IFACE_SUBSYS
,
558 iface
->ipv6_autocfg
, sizeof(iface
->ipv6_autocfg
));
560 sysfs_get_str(iface_kern_id
, ISCSI_IFACE_SUBSYS
,
561 "link_local_addr", iface
->ipv6_linklocal
,
562 sizeof(iface
->ipv6_linklocal
));
564 if (sysfs_get_str(iface_kern_id
, ISCSI_IFACE_SUBSYS
,
566 iface
->linklocal_autocfg
,
567 sizeof(iface
->linklocal_autocfg
))) {
568 /* misspelled in some test kernels */
569 sysfs_get_str(iface_kern_id
, ISCSI_IFACE_SUBSYS
,
570 "link_local_autocfg",
571 iface
->linklocal_autocfg
,
572 sizeof(iface
->linklocal_autocfg
));
575 sysfs_get_str(iface_kern_id
, ISCSI_IFACE_SUBSYS
, "router_addr",
577 sizeof(iface
->ipv6_router
));
580 sysfs_get_uint16(iface_kern_id
, ISCSI_IFACE_SUBSYS
, "mtu",
582 sysfs_get_uint16(iface_kern_id
, ISCSI_IFACE_SUBSYS
, "vlan",
584 sysfs_get_uint8(iface_kern_id
, ISCSI_IFACE_SUBSYS
, "vlan_priority",
585 &iface
->vlan_priority
);
587 if (sscanf(iface_kern_id
, "ipv%d-iface-%u-%u", &iface_type
,
588 &tmp_host_no
, &iface_num
) == 3)
589 iface
->iface_num
= iface_num
;
592 return ISCSI_ERR_SYSFS_LOOKUP
;
597 int iscsi_sysfs_get_hostinfo_by_host_no(struct host_info
*hinfo
)
599 return iscsi_sysfs_read_iface(&hinfo
->iface
, hinfo
->host_no
, NULL
,
603 int iscsi_sysfs_for_each_host(void *data
, int *nr_found
,
604 iscsi_sysfs_host_op_fn
*fn
)
606 struct dirent
**namelist
;
608 struct host_info
*info
;
610 info
= malloc(sizeof(*info
));
612 return ISCSI_ERR_NOMEM
;
614 n
= scandir(ISCSI_HOST_DIR
, &namelist
, trans_filter
,
619 for (i
= 0; i
< n
; i
++) {
620 memset(info
, 0, sizeof(*info
));
621 if (sscanf(namelist
[i
]->d_name
, "host%u", &info
->host_no
) !=
623 log_error("Invalid iscsi host dir: %s",
624 namelist
[i
]->d_name
);
628 iscsi_sysfs_get_hostinfo_by_host_no(info
);
635 for (i
= 0; i
< n
; i
++)
644 int iscsi_sysfs_for_each_iface_on_host(void *data
, uint32_t host_no
,
646 iscsi_sysfs_iface_op_fn
*fn
)
648 struct dirent
**namelist
;
650 struct iface_rec iface
;
651 char devpath
[PATH_SIZE
];
652 char sysfs_path
[PATH_SIZE
];
655 snprintf(id
, sizeof(id
), "host%u", host_no
);
656 if (!sysfs_lookup_devpath_by_subsys_id(devpath
, sizeof(devpath
),
658 log_error("Could not look up host's ifaces via scsi bus.");
659 return ISCSI_ERR_SYSFS_LOOKUP
;
662 sprintf(sysfs_path
, "/sys");
663 strlcat(sysfs_path
, devpath
, sizeof(sysfs_path
));
664 strlcat(sysfs_path
, "/iscsi_iface", sizeof(sysfs_path
));
666 n
= scandir(sysfs_path
, &namelist
, trans_filter
, alphasort
);
668 /* older kernels or some drivers will not have ifaces */
671 for (i
= 0; i
< n
; i
++) {
672 memset(&iface
, 0, sizeof(iface
));
674 iscsi_sysfs_read_iface(&iface
, host_no
, NULL
,
675 namelist
[i
]->d_name
);
676 rc
= fn(data
, &iface
);
682 for (i
= 0; i
< n
; i
++)
689 * sysfs_session_has_leadconn - checks if session has lead conn in kernel
692 * return 1 if session has lead conn and 0 if not.
694 int iscsi_sysfs_session_has_leadconn(uint32_t sid
)
696 char devpath
[PATH_SIZE
];
699 snprintf(id
, sizeof(id
), "connection%u:0", sid
);
700 return sysfs_lookup_devpath_by_subsys_id(devpath
, sizeof(devpath
),
701 ISCSI_CONN_SUBSYS
, id
);
705 * iscsi_sysfs_get_sid_from_path - parse a string for the sid
706 * @session: session path
708 * Given sysfs_device is a directory name of the form:
710 * /sys/devices/platform/hostH/sessionS/targetH:B:I/H:B:I:L
711 * /sys/devices/platform/hostH/sessionS/targetH:B:I
712 * /sys/devices/platform/hostH/sessionS
714 * return the sid S. If just the sid is passed in it will be covnerted
717 int iscsi_sysfs_get_sid_from_path(char *session
)
719 struct sysfs_device
*dev_parent
, *dev
;
721 char devpath
[PATH_SIZE
];
723 if (lstat(session
, &statb
)) {
724 log_debug(1, "Could not stat %s failed with %d",
726 if (index(session
, '/')) {
727 log_error("%s is an invalid session path\n", session
);
730 return atoi(session
);
733 if (!S_ISDIR(statb
.st_mode
) && !S_ISLNK(statb
.st_mode
)) {
734 log_error("%s is not a directory", session
);
738 if (!strncmp(session
, "/sys", 4))
739 strlcpy(devpath
, session
+ 4, sizeof(devpath
));
741 strlcpy(devpath
, session
, sizeof(devpath
));
743 dev
= sysfs_device_get(devpath
);
745 log_error("Could not get dev for %s. Possible sysfs "
746 "incompatibility.\n", devpath
);
750 if (!strncmp(dev
->kernel
, "session", 7))
751 return atoi(dev
->kernel_number
);
753 dev_parent
= sysfs_device_get_parent(dev
);
754 while (dev_parent
!= NULL
) {
755 if (strncmp(dev_parent
->kernel
, "session", 7) == 0)
756 return atoi(dev_parent
->kernel_number
);
757 dev_parent
= sysfs_device_get_parent(dev_parent
);
760 log_error("Unable to find sid in path %s", session
);
764 int iscsi_sysfs_get_sessioninfo_by_id(struct session_info
*info
, char *session
)
767 int ret
, pers_failed
= 0;
770 if (sscanf(session
, "session%d", &info
->sid
) != 1) {
771 log_error("invalid session '%s'", session
);
772 return ISCSI_ERR_INVAL
;
775 ret
= sysfs_get_str(session
, ISCSI_SESSION_SUBSYS
, "targetname",
776 info
->targetname
, sizeof(info
->targetname
));
778 log_error("could not read session targetname: %d", ret
);
779 return ISCSI_ERR_SYSFS_LOOKUP
;
782 ret
= sysfs_get_str(session
, ISCSI_SESSION_SUBSYS
, "username",
783 (info
->chap
).username
,
784 sizeof((info
->chap
).username
));
786 log_debug(5, "could not read username: %d", ret
);
788 ret
= sysfs_get_str(session
, ISCSI_SESSION_SUBSYS
, "password",
789 (info
->chap
).password
,
790 sizeof((info
->chap
).password
));
792 log_debug(5, "could not read password: %d", ret
);
794 ret
= sysfs_get_str(session
, ISCSI_SESSION_SUBSYS
, "username_in",
795 (info
->chap
).username_in
,
796 sizeof((info
->chap
).username_in
));
798 log_debug(5, "could not read username in: %d", ret
);
800 ret
= sysfs_get_str(session
, ISCSI_SESSION_SUBSYS
, "password_in",
801 (info
->chap
).password_in
,
802 sizeof((info
->chap
).password_in
));
804 log_debug(5, "could not read password in: %d", ret
);
806 ret
= sysfs_get_int(session
, ISCSI_SESSION_SUBSYS
, "recovery_tmo",
807 &((info
->tmo
).recovery_tmo
));
809 (info
->tmo
).recovery_tmo
= -1;
811 ret
= sysfs_get_int(session
, ISCSI_SESSION_SUBSYS
, "lu_reset_tmo",
812 &((info
->tmo
).lu_reset_tmo
));
814 (info
->tmo
).lu_reset_tmo
= -1;
816 ret
= sysfs_get_int(session
, ISCSI_SESSION_SUBSYS
, "tgt_reset_tmo",
817 &((info
->tmo
).tgt_reset_tmo
));
819 (info
->tmo
).lu_reset_tmo
= -1;
821 sysfs_get_int(session
, ISCSI_SESSION_SUBSYS
, "abort_tmo",
822 &((info
->tmo
).abort_tmo
));
824 (info
->tmo
).abort_tmo
= -1;
826 ret
= sysfs_get_int(session
, ISCSI_SESSION_SUBSYS
, "tpgt",
829 log_error("could not read session tpgt: %d", ret
);
830 return ISCSI_ERR_SYSFS_LOOKUP
;
833 snprintf(id
, sizeof(id
), ISCSI_CONN_ID
, info
->sid
);
834 /* some HW drivers do not export addr and port */
835 memset(info
->persistent_address
, 0, NI_MAXHOST
);
836 ret
= sysfs_get_str(id
, ISCSI_CONN_SUBSYS
, "persistent_address",
837 info
->persistent_address
,
838 sizeof(info
->persistent_address
));
841 /* older qlogic does not support this */
842 log_debug(5, "could not read pers conn addr: %d", ret
);
845 memset(info
->address
, 0, NI_MAXHOST
);
846 ret
= sysfs_get_str(id
, ISCSI_CONN_SUBSYS
, "address",
847 info
->address
, sizeof(info
->address
));
849 log_debug(5, "could not read curr addr: %d", ret
);
850 /* iser did not export this */
852 strcpy(info
->address
, info
->persistent_address
);
853 } else if (pers_failed
)
855 * for qla if we could not get the persistent addr
856 * we will use the current for both addrs
858 strcpy(info
->persistent_address
, info
->address
);
861 info
->persistent_port
= -1;
862 ret
= sysfs_get_int(id
, ISCSI_CONN_SUBSYS
, "persistent_port",
863 &info
->persistent_port
);
866 log_debug(5, "Could not read pers conn port %d", ret
);
870 ret
= sysfs_get_int(id
, ISCSI_CONN_SUBSYS
, "port", &info
->port
);
872 /* iser did not export this */
874 info
->port
= info
->persistent_port
;
875 log_debug(5, "Could not read curr conn port %d", ret
);
876 } else if (pers_failed
)
878 * for qla if we could not get the persistent addr
879 * we will use the current for both addrs
881 info
->persistent_port
= info
->port
;
884 host_no
= iscsi_sysfs_get_host_no_from_sid(info
->sid
, &ret
);
886 log_error("could not get host_no for session%d: %s.",
887 info
->sid
, iscsi_err_to_str(ret
));
891 iscsi_sysfs_read_iface(&info
->iface
, host_no
, session
, NULL
);
893 log_debug(7, "found targetname %s address %s pers address %s port %d "
894 "pers port %d driver %s iface name %s ipaddress %s "
895 "netdev %s hwaddress %s iname %s",
896 info
->targetname
, info
->address
? info
->address
: "NA",
897 info
->persistent_address
? info
->persistent_address
: "NA",
898 info
->port
, info
->persistent_port
, info
->iface
.transport_name
,
899 info
->iface
.name
, info
->iface
.ipaddress
,
900 info
->iface
.netdev
, info
->iface
.hwaddress
,
905 int iscsi_sysfs_for_each_session(void *data
, int *nr_found
,
906 iscsi_sysfs_session_op_fn
*fn
)
908 struct dirent
**namelist
;
910 struct session_info
*info
;
912 info
= calloc(1, sizeof(*info
));
914 return ISCSI_ERR_NOMEM
;
916 n
= scandir(ISCSI_SESSION_DIR
, &namelist
, trans_filter
,
921 for (i
= 0; i
< n
; i
++) {
922 rc
= iscsi_sysfs_get_sessioninfo_by_id(info
,
923 namelist
[i
]->d_name
);
925 log_error("could not find session info for %s",
926 namelist
[i
]->d_name
);
927 /* raced. session was shutdown while looping */
938 /* if less than zero it means it was not a match */
942 for (i
= 0; i
< n
; i
++)
951 int iscsi_sysfs_get_session_state(char *state
, int sid
)
955 snprintf(id
, sizeof(id
), ISCSI_SESSION_ID
, sid
);
956 if (sysfs_get_str(id
, ISCSI_SESSION_SUBSYS
, "state", state
,
957 SCSI_MAX_STATE_VALUE
))
958 return ISCSI_ERR_SYSFS_LOOKUP
;
962 int iscsi_sysfs_get_host_state(char *state
, int host_no
)
966 snprintf(id
, sizeof(id
), ISCSI_HOST_ID
, host_no
);
967 if (sysfs_get_str(id
, SCSI_HOST_SUBSYS
, "state", state
,
968 SCSI_MAX_STATE_VALUE
))
969 return ISCSI_ERR_SYSFS_LOOKUP
;
973 int iscsi_sysfs_get_device_state(char *state
, int host_no
, int target
, int lun
)
977 snprintf(id
, sizeof(id
), "%d:0:%d:%d", host_no
, target
, lun
);
978 if (sysfs_get_str(id
, SCSI_SUBSYS
, "state", state
,
979 SCSI_MAX_STATE_VALUE
)) {
980 log_debug(3, "Could not read attr state for %s\n", id
);
981 return ISCSI_ERR_SYSFS_LOOKUP
;
987 char *iscsi_sysfs_get_blockdev_from_lun(int host_no
, int target
, int lun
)
989 char devpath
[PATH_SIZE
];
990 char path_full
[PATH_SIZE
];
996 char *blockdev
, *blockdup
= NULL
;
998 snprintf(id
, sizeof(id
), "%d:0:%d:%d", host_no
, target
, lun
);
999 if (!sysfs_lookup_devpath_by_subsys_id(devpath
, sizeof(devpath
),
1001 log_debug(3, "Could not lookup devpath for %s %s\n",
1006 sysfs_len
= strlcpy(path_full
, sysfs_path
, sizeof(path_full
));
1007 if (sysfs_len
>= sizeof(path_full
))
1008 sysfs_len
= sizeof(path_full
) - 1;
1009 strlcat(path_full
, devpath
, sizeof(path_full
));
1011 dirfd
= opendir(path_full
);
1015 while ((dent
= readdir(dirfd
))) {
1016 if (!strcmp(dent
->d_name
, ".") || !strcmp(dent
->d_name
, ".."))
1019 /* not sure what tape looks like */
1020 if (strncmp(dent
->d_name
, "block:", 5))
1023 strlcat(path_full
, "/", sizeof(path_full
));
1024 strlcat(path_full
, dent
->d_name
, sizeof(path_full
));
1026 * 2.6.25 dropped the symlink and now block is a dir.
1028 if (lstat(path_full
, &statbuf
)) {
1029 log_error("Could not stat block path %s err %d\n",
1034 if (S_ISLNK(statbuf
.st_mode
)) {
1035 blockdev
= strchr(dent
->d_name
, ':');
1038 /* increment past colon */
1040 blockdup
= strdup(blockdev
);
1041 } else if (S_ISDIR(statbuf
.st_mode
)) {
1043 struct dirent
*blk_dent
;
1045 /* it should not be this hard should it? :) */
1046 blk_dirfd
= opendir(path_full
);
1048 log_debug(3, "Could not open blk path %s\n",
1053 while ((blk_dent
= readdir(blk_dirfd
))) {
1054 if (!strcmp(blk_dent
->d_name
, ".") ||
1055 !strcmp(blk_dent
->d_name
, ".."))
1057 blockdup
= strdup(blk_dent
->d_name
);
1060 closedir(blk_dirfd
);
1069 static uint32_t get_target_no_from_sid(uint32_t sid
, int *err
)
1071 char devpath
[PATH_SIZE
];
1072 char path_full
[PATH_SIZE
];
1075 struct dirent
*dent
;
1076 uint32_t host
, bus
, target
= 0;
1079 *err
= ISCSI_ERR_SESS_NOT_FOUND
;
1081 snprintf(id
, sizeof(id
), "session%u", sid
);
1082 if (!sysfs_lookup_devpath_by_subsys_id(devpath
, sizeof(devpath
),
1083 ISCSI_SESSION_SUBSYS
, id
)) {
1084 log_debug(3, "Could not lookup devpath for %s %s\n",
1085 ISCSI_SESSION_SUBSYS
, id
);
1090 * This does not seem safe from future changes, but we currently
1091 * want /devices/platform/hostY/sessionX, but we come from the
1092 * /class/iscsi_session/sessionX/device.
1094 sysfs_len
= strlcpy(path_full
, sysfs_path
, sizeof(path_full
));
1095 if (sysfs_len
>= sizeof(path_full
))
1096 sysfs_len
= sizeof(path_full
) - 1;
1097 strlcat(path_full
, devpath
, sizeof(path_full
));
1098 strlcat(path_full
, "/device", sizeof(devpath
));
1100 dirfd
= opendir(path_full
);
1104 while ((dent
= readdir(dirfd
))) {
1105 if (!strcmp(dent
->d_name
, ".") || !strcmp(dent
->d_name
, ".."))
1108 if (strncmp(dent
->d_name
, "target", 6))
1111 if (sscanf(dent
->d_name
, "target%u:%u:%u",
1112 &host
, &bus
, &target
) != 3)
1124 struct iscsi_transport
*iscsi_sysfs_get_transport_by_name(char *transport_name
)
1126 struct iscsi_transport
*t
;
1128 /* sync up kernel and userspace */
1129 if (read_transports())
1132 /* check if the transport is loaded and matches */
1133 list_for_each_entry(t
, &transports
, list
) {
1134 if (t
->handle
&& !strncmp(t
->name
, transport_name
,
1135 ISCSI_TRANSPORT_NAME_MAXLEN
))
1141 /* TODO: replace the following functions with some decent sysfs links */
1142 struct iscsi_transport
*iscsi_sysfs_get_transport_by_hba(uint32_t host_no
)
1144 char name
[ISCSI_TRANSPORT_NAME_MAXLEN
];
1151 snprintf(id
, sizeof(id
), ISCSI_HOST_ID
, host_no
);
1152 rc
= sysfs_get_str(id
, SCSI_HOST_SUBSYS
, "proc_name", name
,
1153 ISCSI_TRANSPORT_NAME_MAXLEN
);
1155 log_error("Could not read proc_name for host%u rc %d.",
1161 * stupid, stupid. We named the transports tcp or iser, but the
1162 * the modules are named iscsi_tcp and iscsi_iser
1164 if (strstr(name
, "iscsi_"))
1165 return iscsi_sysfs_get_transport_by_name(name
+ 6);
1167 return iscsi_sysfs_get_transport_by_name(name
);
1170 struct iscsi_transport
*iscsi_sysfs_get_transport_by_sid(uint32_t sid
)
1175 host_no
= iscsi_sysfs_get_host_no_from_sid(sid
, &err
);
1178 return iscsi_sysfs_get_transport_by_hba(host_no
);
1182 * For connection reinstatement we need to send the exp_statsn from
1183 * the previous connection
1185 * This is only called when the connection is halted so exp_statsn is safe
1186 * to read without racing.
1188 int iscsi_sysfs_get_exp_statsn(int sid
)
1191 uint32_t exp_statsn
= 0;
1193 snprintf(id
, sizeof(id
), ISCSI_CONN_ID
, sid
);
1194 if (sysfs_get_uint(id
, ISCSI_CONN_SUBSYS
, "exp_statsn",
1196 log_error("Could not read expstatsn for sid %d. "
1197 "Using zero for exp_statsn.", sid
);
1203 int iscsi_sysfs_session_supports_nop(int sid
)
1206 uint32_t ping_tmo
= 0;
1208 snprintf(id
, sizeof(id
), ISCSI_CONN_ID
, sid
);
1209 if (sysfs_get_uint(id
, ISCSI_CONN_SUBSYS
, "ping_tmo",
1216 int iscsi_sysfs_for_each_device(void *data
, int host_no
, uint32_t sid
,
1217 void (* fn
)(void *data
, int host_no
,
1218 int target
, int lun
))
1220 struct dirent
**namelist
;
1221 int h
, b
, t
, l
, i
, n
, err
= 0, target
;
1222 char devpath
[PATH_SIZE
];
1224 char path_full
[PATH_SIZE
];
1226 target
= get_target_no_from_sid(sid
, &err
);
1229 snprintf(id
, sizeof(id
), "session%u", sid
);
1230 if (!sysfs_lookup_devpath_by_subsys_id(devpath
, sizeof(devpath
),
1231 ISCSI_SESSION_SUBSYS
, id
)) {
1232 log_debug(3, "Could not lookup devpath for %s %s\n",
1233 ISCSI_SESSION_SUBSYS
, id
);
1234 return ISCSI_ERR_SYSFS_LOOKUP
;
1237 snprintf(path_full
, sizeof(path_full
), "%s%s/device/target%d:0:%d",
1238 sysfs_path
, devpath
, host_no
, target
);
1239 n
= scandir(path_full
, &namelist
, trans_filter
,
1244 for (i
= 0; i
< n
; i
++) {
1245 if (sscanf(namelist
[i
]->d_name
, "%d:%d:%d:%d\n",
1246 &h
, &b
, &t
, &l
) != 4)
1251 for (i
= 0; i
< n
; i
++)
1258 void iscsi_sysfs_set_queue_depth(void *data
, int hostno
, int target
, int lun
)
1262 int err
, qdepth
= *((int *)data
);
1264 snprintf(id
, sizeof(id
), "%d:0:%d:%d", hostno
, target
, lun
);
1265 snprintf(write_buf
, sizeof(write_buf
), "%d", qdepth
);
1266 log_debug(4, "set queue depth for %s to %s", id
, write_buf
);
1268 err
= sysfs_set_param(id
, SCSI_SUBSYS
, "queue_depth", write_buf
,
1270 if (err
&& err
!= EINVAL
)
1271 log_error("Could not queue depth for LUN %d err %d.", lun
, err
);
1274 void iscsi_sysfs_set_device_online(void *data
, int hostno
, int target
, int lun
)
1276 char *write_buf
= "running\n";
1280 snprintf(id
, sizeof(id
), "%d:0:%d:%d", hostno
, target
, lun
);
1281 log_debug(4, "online device %s", id
);
1283 err
= sysfs_set_param(id
, SCSI_SUBSYS
, "state", write_buf
,
1285 if (err
&& err
!= EINVAL
)
1286 /* we should read the state */
1287 log_error("Could not online LUN %d err %d.", lun
, err
);
1290 void iscsi_sysfs_rescan_device(void *data
, int hostno
, int target
, int lun
)
1292 char *write_buf
= "1";
1295 snprintf(id
, sizeof(id
), "%d:0:%d:%d", hostno
, target
, lun
);
1296 log_debug(4, "rescanning device %s", id
);
1297 sysfs_set_param(id
, SCSI_SUBSYS
, "rescan", write_buf
,
1301 pid_t
iscsi_sysfs_scan_host(int hostno
, int async
)
1304 char *write_buf
= "- - -";
1311 log_debug(4, "scanning host%d", hostno
);
1313 snprintf(id
, sizeof(id
), ISCSI_HOST_ID
, hostno
);
1314 sysfs_set_param(id
, SCSI_HOST_SUBSYS
, "scan", write_buf
,
1316 log_debug(4, "scanning host%d completed\n", hostno
);
1317 } else if (pid
> 0) {
1318 log_debug(4, "scanning host%d from pid %d", hostno
, pid
);
1321 * Session is fine, so log the error and let the user
1324 log_error("Could not start scanning process for host %d "
1325 "err %d. Try scanning through sysfs.", hostno
,
1330 struct iscsi_transport
*iscsi_sysfs_get_transport_by_session(char *sys_session
)
1334 if (sscanf(sys_session
, "session%u", &sid
) != 1) {
1335 log_error("invalid session '%s'.", sys_session
);
1339 return iscsi_sysfs_get_transport_by_sid(sid
);
1342 char *iscsi_sysfs_get_iscsi_kernel_version(void)
1344 return sysfs_attr_get_value("/module/scsi_transport_iscsi", "version");
1347 int iscsi_sysfs_check_class_version(void)
1352 version
= iscsi_sysfs_get_iscsi_kernel_version();
1356 log_warning("transport class version %s. iscsid version %s",
1357 version
, ISCSI_VERSION_STR
);
1359 for (i
= 0; i
< strlen(version
); i
++) {
1360 if (version
[i
] == '-')
1364 if (i
== strlen(version
))
1368 * We want to make sure the release and interface are the same.
1369 * It is ok for the svn versions to be different.
1371 if (!strncmp(version
, ISCSI_VERSION_STR
, i
) ||
1372 /* support 2.6.18 */
1373 !strncmp(version
, "1.1", 3))
1377 log_error( "Missing or Invalid version from %s. Make sure a up "
1378 "to date scsi_transport_iscsi module is loaded and a up to"
1379 "date version of iscsid is running. Exiting...",
1380 ISCSI_VERSION_FILE
);