~ubuntu-branches/ubuntu/oneiric/open-iscsi/oneiric

« back to all changes in this revision

Viewing changes to usr/iscsi_sysfs.c

  • Committer: Bazaar Package Importer
  • Author(s): Martin Zobel-Helas
  • Date: 2006-12-03 16:54:21 UTC
  • mto: This revision was merged to the branch mainline in revision 3.
  • Revision ID: james.westby@ubuntu.com-20061203165421-udgd6i05ugt1byrh
Tags: upstream-2.0.730
ImportĀ upstreamĀ versionĀ 2.0.730

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#include <fcntl.h>
 
2
#include <unistd.h>
 
3
#include <stdlib.h>
 
4
#include <stdio.h>
 
5
#include <ctype.h>
 
6
#include <string.h>
 
7
#include <errno.h>
 
8
#include <search.h>
 
9
#include <dirent.h>
 
10
#include <pwd.h>
 
11
#include <sys/types.h>
 
12
#include <sys/wait.h>
 
13
#include <sys/stat.h>
 
14
#include <sys/un.h>
 
15
#include <linux/types.h>
 
16
#include <linux/unistd.h>
 
17
 
 
18
#include "log.h"
 
19
#include "initiator.h"
 
20
#include "transport.h"
 
21
#include "version.h"
 
22
 
 
23
#define ISCSI_TRANSPORT_DIR "/sys/class/iscsi_transport"
 
24
#define ISCSI_MAX_SYSFS_BUFFER NI_MAXHOST
 
25
 
 
26
/* tmp buffer used by sysfs functions */
 
27
static char sysfs_file[PATH_MAX];
 
28
int num_providers = 0;
 
29
struct qelem providers;
 
30
 
 
31
int read_sysfs_file(char *filename, void *value, char *format)
 
32
{
 
33
        FILE *file;
 
34
        char buffer[ISCSI_MAX_SYSFS_BUFFER + 1], *line;
 
35
        int err = 0;
 
36
 
 
37
        file = fopen(filename, "r");
 
38
        if (file) {
 
39
                line = fgets(buffer, sizeof(buffer), file);
 
40
                if (line)
 
41
                        sscanf(buffer, format, value);
 
42
                else {
 
43
                        log_debug(5, "Could not read %s.\n", filename);
 
44
                        err = errno;
 
45
                }
 
46
                fclose(file);
 
47
        } else {
 
48
                log_debug(5, "Could not open %s.\n", filename);
 
49
                err = errno;
 
50
        }
 
51
        return err;
 
52
}
 
53
 
 
54
void init_providers(void)
 
55
{
 
56
        providers.q_forw = &providers;
 
57
        providers.q_back = &providers;
 
58
}
 
59
 
 
60
static int trans_filter(const struct dirent *dir)
 
61
{
 
62
        return strcmp(dir->d_name, ".") && strcmp(dir->d_name, "..");
 
63
}
 
64
 
 
65
static int read_transports(void)
 
66
{
 
67
        struct qelem *item;
 
68
        struct dirent **namelist;
 
69
        char filename[64];
 
70
        int i, n, found, err = 0;
 
71
        iscsi_provider_t *p;
 
72
 
 
73
        log_debug(7, "in %s", __FUNCTION__);
 
74
 
 
75
        if (num_providers == 0)
 
76
                init_providers();
 
77
 
 
78
        n = scandir(ISCSI_TRANSPORT_DIR, &namelist, trans_filter,
 
79
                    alphasort);
 
80
        if (n < 0) {
 
81
                log_error("Could not scan %s.", ISCSI_TRANSPORT_DIR);
 
82
                return n;
 
83
        }
 
84
 
 
85
        for (i = 0; i < n; i++) {
 
86
                found = 0;
 
87
 
 
88
                /* copy existing pr vider to new array */
 
89
                item = providers.q_forw;
 
90
                while (item != &providers) {
 
91
                        p = (iscsi_provider_t *)item;
 
92
 
 
93
                        if (!strcmp(p->name, namelist[i]->d_name)) {
 
94
                                found = 1;
 
95
                                break;
 
96
                        }
 
97
                        item = item->q_forw;
 
98
                }
 
99
 
 
100
                if (found)
 
101
                        continue;
 
102
 
 
103
                /* copy new provider */
 
104
                p = malloc(sizeof(iscsi_provider_t));
 
105
                if (!p)
 
106
                        continue;
 
107
 
 
108
                p->sessions.q_forw = &p->sessions;
 
109
                p->sessions.q_back = &p->sessions;
 
110
 
 
111
                strncpy(p->name, namelist[i]->d_name,
 
112
                        ISCSI_TRANSPORT_NAME_MAXLEN);
 
113
 
 
114
                sprintf(filename, ISCSI_TRANSPORT_DIR"/%s/handle", p->name);
 
115
                err = read_sysfs_file(filename, &p->handle, "%llu\n");
 
116
                if (err)
 
117
                        continue;
 
118
 
 
119
                sprintf(filename, ISCSI_TRANSPORT_DIR"/%s/caps", p->name);
 
120
                err = read_sysfs_file(filename, &p->caps, "0x%x");
 
121
                if (err)
 
122
                        continue;
 
123
 
 
124
                insque(&p->list, &providers);
 
125
        }
 
126
 
 
127
        for (i = 0; i < n; i++)
 
128
                free(namelist[i]);
 
129
        free(namelist);
 
130
        num_providers = n;
 
131
 
 
132
        return 0;
 
133
}
 
134
 
 
135
int get_sessioninfo_by_sysfs_id(int *sid, char *targetname, char *addr,
 
136
                                int *port, int *tpgt, char *session)
 
137
{
 
138
        int ret;
 
139
 
 
140
        if (sscanf(session, "session%d", sid) != 1) {
 
141
                log_error("invalid session '%s'", session);
 
142
                return errno;
 
143
        }
 
144
 
 
145
        memset(sysfs_file, 0, PATH_MAX);
 
146
        sprintf(sysfs_file, "/sys/class/iscsi_session/%s/targetname", session);
 
147
        ret = read_sysfs_file(sysfs_file, targetname, "%s\n");
 
148
        if (ret) {
 
149
                log_error("could not read session targetname: %d", ret);
 
150
                return ret;
 
151
        }
 
152
 
 
153
        memset(sysfs_file, 0, PATH_MAX);
 
154
        sprintf(sysfs_file, "/sys/class/iscsi_session/%s/tpgt", session);
 
155
        ret = read_sysfs_file(sysfs_file, tpgt, "%u\n");
 
156
        if (ret) {
 
157
                log_error("could not read session tpgt: %d", ret);
 
158
                return ret;
 
159
        }
 
160
 
 
161
        /* some HW drivers do not export addr and port */
 
162
        memset(sysfs_file, 0, PATH_MAX);
 
163
        sprintf(sysfs_file, "/sys/class/iscsi_connection/connection%d:0/"
 
164
                "persistent_address", *sid);
 
165
        memset(addr, 0, NI_MAXHOST);
 
166
        ret = read_sysfs_file(sysfs_file, addr, "%s\n");
 
167
        if (ret) {
 
168
                /* fall back to current address */
 
169
                log_debug(5, "could not read pers conn addr: %d", ret);
 
170
                memset(sysfs_file, 0, PATH_MAX);
 
171
                sprintf(sysfs_file,
 
172
                         "/sys/class/iscsi_connection/connection%d:0/address",
 
173
                         *sid);
 
174
                memset(addr, 0, NI_MAXHOST);
 
175
                ret = read_sysfs_file(sysfs_file, addr, "%s\n");
 
176
                if (ret)
 
177
                        log_debug(5, "could not read curr addr: %d", ret);
 
178
        }
 
179
 
 
180
        memset(sysfs_file, 0, PATH_MAX);
 
181
        sprintf(sysfs_file, "/sys/class/iscsi_connection/connection%d:0/"
 
182
                "persistent_port", *sid);
 
183
        *port = -1;
 
184
        ret = read_sysfs_file(sysfs_file, port, "%u\n");
 
185
        if (ret) {
 
186
                /* fall back to current port */
 
187
                log_debug(5, "Could not read pers conn port %d\n", ret);
 
188
                memset(sysfs_file, 0, PATH_MAX);
 
189
                sprintf(sysfs_file,
 
190
                        "/sys/class/iscsi_connection/connection%d:0/port",
 
191
                        *sid);
 
192
                *port = -1;
 
193
                ret = read_sysfs_file(sysfs_file, port, "%u\n");
 
194
                if (ret)
 
195
                        log_debug(5, "Could not read curr conn port %d\n", ret);
 
196
        }
 
197
 
 
198
        log_debug(7, "found targetname %s address %s port %d\n",
 
199
                  targetname, addr ? addr : "NA", *port);
 
200
        return 0;
 
201
}
 
202
 
 
203
int sysfs_for_each_session(void *data, int *nr_found,
 
204
                           int (* fn)(void *, char *, int, char *, int, int))
 
205
{
 
206
        DIR *dirfd;
 
207
        struct dirent *dent;
 
208
        int rc = 0, sid, port, tpgt;
 
209
        char *targetname, *address;
 
210
 
 
211
        targetname = malloc(TARGET_NAME_MAXLEN + 1);
 
212
        if (!targetname)
 
213
                return -ENOMEM;
 
214
 
 
215
        address = malloc(NI_MAXHOST + 1);
 
216
        if (!address) {
 
217
                rc = -ENOMEM;
 
218
                goto free_target;
 
219
        }
 
220
 
 
221
        sprintf(sysfs_file, "/sys/class/iscsi_session");
 
222
        dirfd = opendir(sysfs_file);
 
223
        if (!dirfd) {
 
224
                rc = -EINVAL;
 
225
                goto free_address;
 
226
        }
 
227
 
 
228
        while ((dent = readdir(dirfd))) {
 
229
                if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, ".."))
 
230
                        continue;
 
231
 
 
232
                rc = get_sessioninfo_by_sysfs_id(&sid, targetname, address,
 
233
                                                 &port, &tpgt, dent->d_name);
 
234
                if (rc < 0) {
 
235
                        log_error("could not find session info for %s",
 
236
                                   dent->d_name);
 
237
                        continue;
 
238
                }
 
239
 
 
240
                rc = fn(data, targetname, tpgt, address, port, sid);
 
241
                if (rc != 0)
 
242
                        break;
 
243
                (*nr_found)++;
 
244
        }
 
245
 
 
246
        closedir(dirfd);
 
247
free_address:
 
248
        free(address);
 
249
free_target:
 
250
        free(targetname);
 
251
        return rc;
 
252
}
 
253
 
 
254
uint32_t get_host_no_from_sid(uint32_t sid, int *err)
 
255
{
 
256
        char buf[PATH_MAX], path[PATH_MAX], *tmp;
 
257
        uint32_t host_no;
 
258
 
 
259
        *err = 0;
 
260
 
 
261
        memset(buf, 0, PATH_MAX);
 
262
        memset(path, 0, PATH_MAX);
 
263
        sprintf(path, "/sys/class/iscsi_session/session%d/device", sid);
 
264
        if (readlink(path, buf, PATH_MAX) < 0) {
 
265
                log_error("Could not get link for %s\n", path);
 
266
                *err = errno;
 
267
                return 0;
 
268
        }
 
269
 
 
270
        /* buf will be .....bus_info/hostX/sessionY */
 
271
 
 
272
        /* find hostX */
 
273
        tmp = strrchr(buf, '/');
 
274
        *tmp = '\0';
 
275
 
 
276
        /* find bus and increment past it */
 
277
        tmp = strrchr(buf, '/');
 
278
        tmp++;
 
279
 
 
280
        if (sscanf(tmp, "host%u", &host_no) != 1) {
 
281
                log_error("Could not get host for sid %u\n", sid);
 
282
                *err = -ENXIO;
 
283
                return 0;
 
284
        }
 
285
 
 
286
        return host_no;
 
287
}
 
288
 
 
289
iscsi_provider_t *get_transport_by_name(char *transport_name)
 
290
{
 
291
        struct qelem *pitem;
 
292
        iscsi_provider_t *p;
 
293
 
 
294
        /* sync up kernel and userspace */
 
295
        read_transports();
 
296
 
 
297
        /* check if the transport is loaded */
 
298
        pitem = providers.q_forw;
 
299
        while (pitem != &providers) {
 
300
                p = (iscsi_provider_t *)pitem;
 
301
 
 
302
                if (p->handle && !strncmp(p->name, transport_name,
 
303
                                          ISCSI_TRANSPORT_NAME_MAXLEN))
 
304
                        return p;
 
305
                pitem = pitem->q_forw;
 
306
        }
 
307
        return NULL;
 
308
}
 
309
 
 
310
/* TODO: replace the following functions with some decent sysfs links */
 
311
iscsi_provider_t *get_transport_by_hba(long host_no)
 
312
{
 
313
        char name[ISCSI_TRANSPORT_NAME_MAXLEN];
 
314
        int rc;
 
315
 
 
316
        if (host_no == -1)
 
317
                return NULL;
 
318
 
 
319
        memset(sysfs_file, 0, PATH_MAX);
 
320
        sprintf(sysfs_file, "/sys/class/scsi_host/host%lu/proc_name", host_no);
 
321
        rc = read_sysfs_file(sysfs_file, name, "%s\n");
 
322
        if (rc) {
 
323
                log_error("Could not read %s rc %d\n", sysfs_file, rc);
 
324
                return NULL;
 
325
        }
 
326
 
 
327
        /*
 
328
         * stupid, stupid. We named the transports tcp or iser, but the
 
329
         * the modules are named iscsi_tcp and iscsi_iser
 
330
         */
 
331
        if (strstr(name, "iscsi_"))
 
332
                return get_transport_by_name(name + 6);
 
333
        else
 
334
                return get_transport_by_name(name);
 
335
}
 
336
 
 
337
iscsi_provider_t *get_transport_by_sid(uint32_t sid)
 
338
{
 
339
        uint32_t host_no;
 
340
        int err;
 
341
 
 
342
        host_no = get_host_no_from_sid(sid, &err);
 
343
        if (err)
 
344
                return NULL;
 
345
        return get_transport_by_hba(host_no);
 
346
}
 
347
 
 
348
/*
 
349
 * For connection reinstatement we need to send the exp_statsn from
 
350
 * the previous connection
 
351
 *
 
352
 * This is only called when the connection is halted so exp_statsn is safe
 
353
 * to read without racing.
 
354
 */
 
355
int set_exp_statsn(iscsi_conn_t *conn)
 
356
{
 
357
        sprintf(sysfs_file,
 
358
                "/sys/class/iscsi_connection/connection%d:%d/exp_statsn",
 
359
                conn->session->id, conn->id);
 
360
        if (read_sysfs_file(sysfs_file, &conn->exp_statsn, "%u\n")) {
 
361
                log_error("Could not read %s. Using zero fpr exp_statsn\n",
 
362
                          sysfs_file);
 
363
                conn->exp_statsn = 0;
 
364
        }
 
365
        return 0;
 
366
}
 
367
 
 
368
int sysfs_for_each_device(int host_no, uint32_t sid,
 
369
                          void (* fn)(int host_no, int lun))
 
370
{
 
371
        DIR *dirfd;
 
372
        struct dirent *dent;
 
373
        int h, b, t, l;
 
374
 
 
375
        sprintf(sysfs_file, "/sys/class/iscsi_session/session%d/device/target%d:0:0", sid, host_no);
 
376
        dirfd = opendir(sysfs_file);
 
377
        if (!dirfd)
 
378
                return errno;
 
379
 
 
380
        while ((dent = readdir(dirfd))) {
 
381
                if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, ".."))
 
382
                        continue;
 
383
 
 
384
                if (sscanf(dent->d_name, "%d:%d:%d:%d\n", &h, &b, &t, &l) != 4)
 
385
                        continue;
 
386
                fn(h, l);
 
387
        }
 
388
        return 0;
 
389
}
 
390
 
 
391
void set_device_online(int hostno, int lun)
 
392
{
 
393
        int fd;
 
394
 
 
395
        sprintf(sysfs_file, "/sys/bus/scsi/devices/%d:0:0:%d/state",
 
396
                hostno, lun);
 
397
        fd = open(sysfs_file, O_WRONLY);
 
398
        if (fd < 0)
 
399
                return;
 
400
        log_debug(4, "online device using %s", sysfs_file);
 
401
        if (write(fd, "running\n", 8) == -1 && errno != EINVAL) 
 
402
                /* we should read the state */
 
403
                log_error("Could not online LUN %d err %d\n",
 
404
                          lun, errno);
 
405
        close(fd);
 
406
}
 
407
 
 
408
/* TODO: remove this when we fix shutdown */
 
409
void delete_device(int hostno, int lun)
 
410
{
 
411
        int fd;
 
412
 
 
413
        sprintf(sysfs_file, "/sys/bus/scsi/devices/%d:0:0:%d/delete",
 
414
                hostno, lun);
 
415
        fd = open(sysfs_file, O_WRONLY);
 
416
        if (fd < 0)
 
417
                return;
 
418
        log_debug(4, "deleting device using %s", sysfs_file);
 
419
        write(fd, "1", 1);
 
420
        close(fd);
 
421
}
 
422
 
 
423
/*
 
424
 * Scan a session from usersapce using sysfs
 
425
 */
 
426
pid_t scan_host(iscsi_session_t *session)
 
427
{
 
428
        int hostno = session->hostno;
 
429
        pid_t pid;
 
430
        int fd;
 
431
 
 
432
        sprintf(sysfs_file, "/sys/class/scsi_host/host%d/scan",
 
433
                session->hostno);
 
434
        fd = open(sysfs_file, O_WRONLY);
 
435
        if (fd < 0) {
 
436
                log_error("could not scan scsi host%d\n", hostno);
 
437
                return -1;
 
438
        }
 
439
 
 
440
        pid = fork();
 
441
        if (pid == 0) {
 
442
                /* child */
 
443
                log_debug(4, "scanning host%d using %s",hostno,
 
444
                          sysfs_file);
 
445
                write(fd, "- - -", 5);
 
446
                log_debug(4, "scanning host%d completed\n", hostno);
 
447
        } else if (pid > 0) {
 
448
                log_debug(4, "scanning host%d from pid %d", hostno, pid);
 
449
        } else
 
450
                /*
 
451
                 * Session is fine, so log the error and let the user
 
452
                 * scan by hand
 
453
                  */
 
454
                log_error("Could not start scanning process for host %d "
 
455
                          "err %d. Try scanning through sysfs\n", hostno,
 
456
                          errno);
 
457
 
 
458
        close(fd);
 
459
        return pid;
 
460
}
 
461
 
 
462
iscsi_provider_t *get_transport_by_session(char *sys_session)
 
463
{
 
464
        uint32_t sid;
 
465
 
 
466
        if (sscanf(sys_session, "session%u", &sid) != 1) {
 
467
                log_error("invalid session '%s'", sys_session);
 
468
                return NULL;
 
469
        }
 
470
 
 
471
        return get_transport_by_sid(sid);
 
472
}
 
473
 
 
474
void check_class_version(void)
 
475
{
 
476
        char version[20];
 
477
        int i;
 
478
 
 
479
        if (read_sysfs_file(ISCSI_VERSION_FILE, version, "%s\n"))
 
480
                goto fail;
 
481
 
 
482
        log_warning("transport class version %s. iscsid version %s\n",
 
483
                    version, ISCSI_VERSION_STR);
 
484
 
 
485
        for (i = 0; i < strlen(version); i++) {
 
486
                if (version[i] == '-')
 
487
                        break;
 
488
        }
 
489
 
 
490
        if (i == strlen(version))
 
491
                goto fail;
 
492
 
 
493
        /*
 
494
         * We want to make sure the release and interface are the same.
 
495
         * It is ok for the svn versions to be different.
 
496
         */
 
497
        if (!strncmp(version, ISCSI_VERSION_STR, i) ||
 
498
           /* support 2.6.18 */
 
499
            !strncmp(version, "1.1", 3))
 
500
                return;
 
501
 
 
502
fail:
 
503
        log_error("Invalid version from %s. Make sure a up to date "
 
504
                  "scsi_transport_iscsi module is loaded and a up to"
 
505
                  "date version of iscsid is running. Exiting...\n",
 
506
                  ISCSI_VERSION_FILE);
 
507
        exit(1);
 
508
}