usr/config.h: fix comment for struct iscsi_session_timeout_config
[open-iscsi.git] / usr / iscsi_sysfs.c
blob961cefd802866a0bcdc52fb2fb4883a816d4a50e
1 /*
2 * iSCSI sysfs
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.
17 #include <fcntl.h>
18 #include <unistd.h>
19 #include <stdlib.h>
20 #include <stdio.h>
21 #include <ctype.h>
22 #include <string.h>
23 #include <errno.h>
24 #include <dirent.h>
25 #include <sys/types.h>
26 #include <sys/stat.h>
28 #include "log.h"
29 #include "initiator.h"
30 #include "transport.h"
31 #include "idbm.h"
32 #include "version.h"
33 #include "iscsi_sysfs.h"
34 #include "sysdeps.h"
35 #include "iscsi_settings.h"
36 #include "iface.h"
37 #include "session_info.h"
38 #include "host.h"
39 #include "iscsi_err.h"
42 * TODO: remove the _DIR defines and search for subsys dirs like
43 * is done in sysfs.c.
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) {
73 list_del(&t->list);
74 free(t);
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;
86 int i, n, found;
87 struct iscsi_transport *t;
89 log_debug(7, "in %s", __FUNCTION__);
91 n = scandir(ISCSI_TRANSPORT_DIR, &namelist, trans_filter,
92 alphasort);
93 if (n < 0) {
94 log_error("Could not scan %s.", ISCSI_TRANSPORT_DIR);
95 return n;
98 for (i = 0; i < n; i++) {
99 found = 0;
101 list_for_each_entry(t, &transports, list) {
102 if (!strcmp(t->name, namelist[i]->d_name)) {
103 found = 1;
104 break;
108 if (!found) {
109 /* copy new transport */
110 t = malloc(sizeof(*t));
111 if (!t)
112 continue;
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)) {
121 free(t);
122 return -1;
124 } else
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))
131 free(t);
132 else
133 log_error("Could not update %s.\n",
134 t->name);
135 continue;
138 if (sysfs_get_uint(t->name, ISCSI_TRANSPORT_SUBSYS,
139 "caps", &t->caps)) {
140 if (list_empty(&t->list))
141 free(t);
142 else
143 log_error("Could not update %s.\n",
144 t->name);
145 continue;
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++)
159 free(namelist[i]);
160 free(namelist);
161 num_transports = n;
163 return 0;
166 /* caller must check lengths */
167 void iscsi_sysfs_get_auth_conf(int sid, struct iscsi_auth_config *conf)
169 char id[NAME_SIZE];
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)
194 char id[NAME_SIZE];
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)
212 char id[NAME_SIZE];
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",
229 &conf->InitialR2T);
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];
238 char id[NAME_SIZE];
240 *err = 0;
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;
247 return 0;
250 session_dev = sysfs_device_get(devpath);
251 if (!session_dev) {
252 log_error("Could not get dev for %s. Possible sysfs "
253 "incompatibility.\n", id);
254 *err = ISCSI_ERR_SYSFS_LOOKUP;
255 return 0;
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,
263 SCSI_SUBSYS);
264 if (!host_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;
271 break;
273 dev_parent = sysfs_device_get_parent(dev_parent);
276 if (!host_dev) {
277 log_error("Could not get host dev for %s. Possible "
278 "sysfs incompatibility.\n", id);
279 *err = ISCSI_ERR_SYSFS_LOOKUP;
280 return 0;
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;
294 return 1;
296 return 0;
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;
305 *rc = 0;
307 info = calloc(1, sizeof(*info));
308 if (!info) {
309 *rc = ISCSI_ERR_NOMEM;
310 return -1;
312 strcpy(info->iface.netdev, netdev);
314 local_rc = iscsi_sysfs_for_each_host(info, &nr_found,
315 __get_host_no_from_netdev);
316 if (local_rc == 1)
317 host_no = info->host_no;
318 else
319 *rc = ISCSI_ERR_HOST_NOT_FOUND;
320 free(info);
321 return host_no;
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;
330 return 1;
332 return 0;
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;
341 *rc = 0;
343 info = calloc(1, sizeof(*info));
344 if (!info) {
345 *rc = ISCSI_ERR_NOMEM;
346 return -1;
348 strcpy(info->iface.hwaddress, hwaddress);
350 local_rc = iscsi_sysfs_for_each_host(info, &nr_found,
351 __get_host_no_from_hwaddress);
352 if (local_rc == 1)
353 host_no = info->host_no;
354 else
355 *rc = ISCSI_ERR_HOST_NOT_FOUND;
356 free(info);
357 return host_no;
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;
366 return 1;
368 return 0;
371 static uint32_t get_host_no_from_ipaddress(char *address, int *rc)
373 uint32_t host_no = -1;
374 struct host_info *info;
375 int nr_found;
376 int local_rc;
378 *rc = 0;
380 info = calloc(1, sizeof(*info));
381 if (!info) {
382 *rc = ISCSI_ERR_NOMEM;
383 return -1;
385 strcpy(info->iface.ipaddress, address);
387 local_rc = iscsi_sysfs_for_each_host(info, &nr_found,
388 __get_host_no_from_ipaddress);
389 if (local_rc == 1)
390 host_no = info->host_no;
391 else
392 *rc = ISCSI_ERR_HOST_NOT_FOUND;
393 free(info);
394 return host_no;
397 uint32_t iscsi_sysfs_get_host_no_from_hwinfo(struct iface_rec *iface, int *rc)
399 int tmp_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);
412 else
413 tmp_rc = ISCSI_ERR_INVAL;
415 *rc = tmp_rc;
416 return host_no;
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
423 * qla4xxx.
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;
431 int ret, iface_type;
433 t = iscsi_sysfs_get_transport_by_hba(host_no);
434 if (!t)
435 log_debug(7, "could not get transport name for host%d",
436 host_no);
437 else
438 strcpy(iface->transport_name, t->name);
440 snprintf(host_id, sizeof(host_id), ISCSI_HOST_ID, host_no);
442 * backward compat
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));
448 if (ret)
449 log_debug(7, "could not read hwaddress for host%d\n", host_no);
451 if (iface_kern_id)
452 ret = sysfs_get_str(iface_kern_id, ISCSI_IFACE_SUBSYS,
453 "ipaddress",
454 iface->ipaddress, sizeof(iface->ipaddress));
455 else
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));
459 if (ret)
460 log_debug(7, "could not read local address for host%d\n",
461 host_no);
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));
466 if (ret)
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.
477 ret = 1;
478 memset(iface->iname, 0, sizeof(iface->iname));
479 if (session) {
480 ret = sysfs_get_str(session, ISCSI_SESSION_SUBSYS,
481 "initiatorname",
482 iface->iname, sizeof(iface->iname));
484 * qlaxxx will not set this at the session level so we
485 * always drop down for it.
487 * And.
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
491 * we will drop down.
495 if (ret) {
496 ret = sysfs_get_str(host_id, ISCSI_HOST_SUBSYS, "initiatorname",
497 iface->iname, sizeof(iface->iname));
498 if (ret)
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 */
509 ret = 0;
513 * this is on the session, because we support multiple bindings
514 * per device.
516 memset(iface->name, 0, sizeof(iface->name));
517 if (session) {
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));
525 if (ret) {
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
532 * older kernels.
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",
537 iface_str(iface));
541 if (!iface_kern_id)
542 goto done;
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));
555 } else {
556 sysfs_get_str(iface_kern_id, ISCSI_IFACE_SUBSYS,
557 "ipaddr_autocfg",
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,
565 "linklocal_autocfg",
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",
576 iface->ipv6_router,
577 sizeof(iface->ipv6_router));
580 sysfs_get_uint16(iface_kern_id, ISCSI_IFACE_SUBSYS, "mtu",
581 &iface->mtu);
582 sysfs_get_uint16(iface_kern_id, ISCSI_IFACE_SUBSYS, "vlan",
583 &iface->vlan_id);
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;
590 done:
591 if (ret)
592 return ISCSI_ERR_SYSFS_LOOKUP;
593 else
594 return 0;
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,
600 NULL);
603 int iscsi_sysfs_for_each_host(void *data, int *nr_found,
604 iscsi_sysfs_host_op_fn *fn)
606 struct dirent **namelist;
607 int rc = 0, i, n;
608 struct host_info *info;
610 info = malloc(sizeof(*info));
611 if (!info)
612 return ISCSI_ERR_NOMEM;
614 n = scandir(ISCSI_HOST_DIR, &namelist, trans_filter,
615 alphasort);
616 if (n <= 0)
617 goto free_info;
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) !=
622 1) {
623 log_error("Invalid iscsi host dir: %s",
624 namelist[i]->d_name);
625 break;
628 iscsi_sysfs_get_hostinfo_by_host_no(info);
629 rc = fn(data, info);
630 if (rc != 0)
631 break;
632 (*nr_found)++;
635 for (i = 0; i < n; i++)
636 free(namelist[i]);
637 free(namelist);
639 free_info:
640 free(info);
641 return rc;
644 int iscsi_sysfs_for_each_iface_on_host(void *data, uint32_t host_no,
645 int *nr_found,
646 iscsi_sysfs_iface_op_fn *fn)
648 struct dirent **namelist;
649 int rc = 0, i, n;
650 struct iface_rec iface;
651 char devpath[PATH_SIZE];
652 char sysfs_path[PATH_SIZE];
653 char id[NAME_SIZE];
655 snprintf(id, sizeof(id), "host%u", host_no);
656 if (!sysfs_lookup_devpath_by_subsys_id(devpath, sizeof(devpath),
657 SCSI_SUBSYS, id)) {
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);
667 if (n <= 0)
668 /* older kernels or some drivers will not have ifaces */
669 return 0;
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);
677 if (rc != 0)
678 break;
679 (*nr_found)++;
682 for (i = 0; i < n; i++)
683 free(namelist[i]);
684 free(namelist);
685 return rc;
689 * sysfs_session_has_leadconn - checks if session has lead conn in kernel
690 * @sid: session id
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];
697 char id[NAME_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
715 * to a int.
717 int iscsi_sysfs_get_sid_from_path(char *session)
719 struct sysfs_device *dev_parent, *dev;
720 struct stat statb;
721 char devpath[PATH_SIZE];
723 if (lstat(session, &statb)) {
724 log_debug(1, "Could not stat %s failed with %d",
725 session, errno);
726 if (index(session, '/')) {
727 log_error("%s is an invalid session path\n", session);
728 exit(1);
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);
735 exit(1);
738 if (!strncmp(session, "/sys", 4))
739 strlcpy(devpath, session + 4, sizeof(devpath));
740 else
741 strlcpy(devpath, session, sizeof(devpath));
743 dev = sysfs_device_get(devpath);
744 if (!dev) {
745 log_error("Could not get dev for %s. Possible sysfs "
746 "incompatibility.\n", devpath);
747 return -1;
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);
761 return -1;
764 int iscsi_sysfs_get_sessioninfo_by_id(struct session_info *info, char *session)
766 char id[NAME_SIZE];
767 int ret, pers_failed = 0;
768 uint32_t host_no;
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));
777 if (ret) {
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));
785 if (ret)
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));
791 if (ret)
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));
797 if (ret)
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));
803 if (ret)
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));
808 if (ret)
809 (info->tmo).recovery_tmo = -1;
811 ret = sysfs_get_int(session, ISCSI_SESSION_SUBSYS, "lu_reset_tmo",
812 &((info->tmo).lu_reset_tmo));
813 if (ret)
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));
818 if (ret)
819 (info->tmo).lu_reset_tmo = -1;
821 sysfs_get_int(session, ISCSI_SESSION_SUBSYS, "abort_tmo",
822 &((info->tmo).abort_tmo));
823 if (ret)
824 (info->tmo).abort_tmo = -1;
826 ret = sysfs_get_int(session, ISCSI_SESSION_SUBSYS, "tpgt",
827 &info->tpgt);
828 if (ret) {
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));
839 if (ret) {
840 pers_failed = 1;
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));
848 if (ret) {
849 log_debug(5, "could not read curr addr: %d", ret);
850 /* iser did not export this */
851 if (!pers_failed)
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);
859 pers_failed = 0;
861 info->persistent_port = -1;
862 ret = sysfs_get_int(id, ISCSI_CONN_SUBSYS, "persistent_port",
863 &info->persistent_port);
864 if (ret) {
865 pers_failed = 1;
866 log_debug(5, "Could not read pers conn port %d", ret);
869 info->port = -1;
870 ret = sysfs_get_int(id, ISCSI_CONN_SUBSYS, "port", &info->port);
871 if (ret) {
872 /* iser did not export this */
873 if (!pers_failed)
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;
883 ret = 0;
884 host_no = iscsi_sysfs_get_host_no_from_sid(info->sid, &ret);
885 if (ret) {
886 log_error("could not get host_no for session%d: %s.",
887 info->sid, iscsi_err_to_str(ret));
888 return 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,
901 info->iface.iname);
902 return 0;
905 int iscsi_sysfs_for_each_session(void *data, int *nr_found,
906 iscsi_sysfs_session_op_fn *fn)
908 struct dirent **namelist;
909 int rc = 0, n, i;
910 struct session_info *info;
912 info = calloc(1, sizeof(*info));
913 if (!info)
914 return ISCSI_ERR_NOMEM;
916 n = scandir(ISCSI_SESSION_DIR, &namelist, trans_filter,
917 alphasort);
918 if (n <= 0)
919 goto free_info;
921 for (i = 0; i < n; i++) {
922 rc = iscsi_sysfs_get_sessioninfo_by_id(info,
923 namelist[i]->d_name);
924 if (rc) {
925 log_error("could not find session info for %s",
926 namelist[i]->d_name);
927 /* raced. session was shutdown while looping */
928 rc = 0;
929 continue;
932 rc = fn(data, info);
933 if (rc > 0)
934 break;
935 else if (rc == 0)
936 (*nr_found)++;
937 else
938 /* if less than zero it means it was not a match */
939 rc = 0;
942 for (i = 0; i < n; i++)
943 free(namelist[i]);
944 free(namelist);
946 free_info:
947 free(info);
948 return rc;
951 int iscsi_sysfs_get_session_state(char *state, int sid)
953 char id[NAME_SIZE];
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;
959 return 0;
962 int iscsi_sysfs_get_host_state(char *state, int host_no)
964 char id[NAME_SIZE];
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;
970 return 0;
973 int iscsi_sysfs_get_device_state(char *state, int host_no, int target, int lun)
975 char id[NAME_SIZE];
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;
984 return 0;
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];
991 char id[NAME_SIZE];
992 DIR *dirfd;
993 struct dirent *dent;
994 size_t sysfs_len;
995 struct stat statbuf;
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),
1000 SCSI_SUBSYS, id)) {
1001 log_debug(3, "Could not lookup devpath for %s %s\n",
1002 SCSI_SUBSYS, id);
1003 return NULL;
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);
1012 if (!dirfd)
1013 return NULL;
1015 while ((dent = readdir(dirfd))) {
1016 if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, ".."))
1017 continue;
1019 /* not sure what tape looks like */
1020 if (strncmp(dent->d_name, "block:", 5))
1021 continue;
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",
1030 path_full, errno);
1031 break;
1034 if (S_ISLNK(statbuf.st_mode)) {
1035 blockdev = strchr(dent->d_name, ':');
1036 if (!blockdev)
1037 break;
1038 /* increment past colon */
1039 blockdev++;
1040 blockdup = strdup(blockdev);
1041 } else if (S_ISDIR(statbuf.st_mode)) {
1042 DIR *blk_dirfd;
1043 struct dirent *blk_dent;
1045 /* it should not be this hard should it? :) */
1046 blk_dirfd = opendir(path_full);
1047 if (!blk_dirfd) {
1048 log_debug(3, "Could not open blk path %s\n",
1049 path_full);
1050 break;
1053 while ((blk_dent = readdir(blk_dirfd))) {
1054 if (!strcmp(blk_dent->d_name, ".") ||
1055 !strcmp(blk_dent->d_name, ".."))
1056 continue;
1057 blockdup = strdup(blk_dent->d_name);
1058 break;
1060 closedir(blk_dirfd);
1063 break;
1065 closedir(dirfd);
1066 return blockdup;
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];
1073 char id[NAME_SIZE];
1074 DIR *dirfd;
1075 struct dirent *dent;
1076 uint32_t host, bus, target = 0;
1077 size_t sysfs_len;
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);
1086 return 0;
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);
1101 if (!dirfd)
1102 return 0;
1104 while ((dent = readdir(dirfd))) {
1105 if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, ".."))
1106 continue;
1108 if (strncmp(dent->d_name, "target", 6))
1109 continue;
1111 if (sscanf(dent->d_name, "target%u:%u:%u",
1112 &host, &bus, &target) != 3)
1113 break;
1115 *err = 0;
1116 break;
1119 closedir(dirfd);
1120 return target;
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())
1130 return NULL;
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))
1136 return t;
1138 return NULL;
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];
1145 char id[NAME_SIZE];
1146 int rc;
1148 if (host_no == -1)
1149 return NULL;
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);
1154 if (rc) {
1155 log_error("Could not read proc_name for host%u rc %d.",
1156 host_no, rc);
1157 return NULL;
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);
1166 else
1167 return iscsi_sysfs_get_transport_by_name(name);
1170 struct iscsi_transport *iscsi_sysfs_get_transport_by_sid(uint32_t sid)
1172 uint32_t host_no;
1173 int err;
1175 host_no = iscsi_sysfs_get_host_no_from_sid(sid, &err);
1176 if (err)
1177 return NULL;
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)
1190 char id[NAME_SIZE];
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",
1195 &exp_statsn)) {
1196 log_error("Could not read expstatsn for sid %d. "
1197 "Using zero for exp_statsn.", sid);
1198 exp_statsn = 0;
1200 return exp_statsn;
1203 int iscsi_sysfs_session_supports_nop(int sid)
1205 char id[NAME_SIZE];
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",
1210 &ping_tmo)) {
1211 return 0;
1213 return 1;
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];
1223 char id[NAME_SIZE];
1224 char path_full[PATH_SIZE];
1226 target = get_target_no_from_sid(sid, &err);
1227 if (err)
1228 return 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,
1240 alphasort);
1241 if (n <= 0)
1242 return 0;
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)
1247 continue;
1248 fn(data, h, t, l);
1251 for (i = 0; i < n; i++)
1252 free(namelist[i]);
1253 free(namelist);
1255 return 0;
1258 void iscsi_sysfs_set_queue_depth(void *data, int hostno, int target, int lun)
1260 char id[NAME_SIZE];
1261 char write_buf[20];
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,
1269 strlen(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";
1277 char id[NAME_SIZE];
1278 int err;
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,
1284 strlen(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";
1293 char id[NAME_SIZE];
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,
1298 strlen(write_buf));
1301 pid_t iscsi_sysfs_scan_host(int hostno, int async)
1303 char id[NAME_SIZE];
1304 char *write_buf = "- - -";
1305 pid_t pid = 0;
1307 if (async)
1308 pid = fork();
1309 if (pid == 0) {
1310 /* child */
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,
1315 strlen(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);
1319 } else
1321 * Session is fine, so log the error and let the user
1322 * scan by hand
1324 log_error("Could not start scanning process for host %d "
1325 "err %d. Try scanning through sysfs.", hostno,
1326 errno);
1327 return pid;
1330 struct iscsi_transport *iscsi_sysfs_get_transport_by_session(char *sys_session)
1332 uint32_t sid;
1334 if (sscanf(sys_session, "session%u", &sid) != 1) {
1335 log_error("invalid session '%s'.", sys_session);
1336 return NULL;
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)
1349 char *version;
1350 int i;
1352 version = iscsi_sysfs_get_iscsi_kernel_version();
1353 if (!version)
1354 goto fail;
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] == '-')
1361 break;
1364 if (i == strlen(version))
1365 goto fail;
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))
1374 return 0;
1376 fail:
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);
1381 return -1;