Sysfs interface for targets made uniform. ISCSI-SCST made confirming the uniformity...
[mirror/scst/.git] / iscsi-scst / usr / iscsi_adm.c
1 /*
2  *  iscsi_adm - manage iSCSI-SCST Target software.
3  *
4  *  Copyright (C) 2004 - 2005 FUJITA Tomonori <tomof@acm.org>
5  *  Copyright (C) 2007 - 2009 Vladislav Bolkhovitin
6  *  Copyright (C) 2007 - 2009 ID7 Ltd.
7  *
8  *  This program is free software; you can redistribute it and/or
9  *  modify it under the terms of the GNU General Public License
10  *  as published by the Free Software Foundation.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  *  GNU General Public License for more details.
16  */
17
18 #include <ctype.h>
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <getopt.h>
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <unistd.h>
25 #include <string.h>
26
27 #include <sys/socket.h>
28 #include <sys/stat.h>
29 #include <sys/types.h>
30 #include <sys/un.h>
31
32 #include "iscsid.h"
33 #include "iscsi_adm.h"
34
35 #define SET_TARGET      (1 << 0)
36 #define SET_SESSION     (1 << 1)
37 #define SET_CONNECTION  (1 << 2)
38 #define SET_USER        (1 << 4)
39
40 typedef int (user_handle_fn_t)(struct iscsi_adm_req *req, char *user, char *pass);
41
42 enum iscsi_adm_op {
43         OP_NEW,
44         OP_DELETE,
45         OP_UPDATE,
46         OP_SHOW,
47 };
48
49 static char program_name[] = "iscsi-scst-adm";
50
51 static struct option const long_options[] =
52 {
53         {"op", required_argument, NULL, 'o'},
54         {"tid", required_argument, NULL, 't'},
55         {"sid", required_argument, NULL, 's'},
56         {"cid", required_argument, NULL, 'c'},
57         {"params", required_argument, NULL, 'p'},
58         {"user", no_argument, NULL, 'u'},
59         {"version", no_argument, NULL, 'v'},
60         {"help", no_argument, NULL, 'h'},
61         {NULL, 0, NULL, 0},
62 };
63
64 static void usage(int status)
65 {
66         if (status != 0)
67                 fprintf(stderr, "Try `%s --help' for more information.\n", program_name);
68         else {
69                 printf("Usage: %s [OPTION]\n", program_name);
70                 printf("\
71 iSCSI-SCST Target Administration Utility.\n\
72 \n\
73   --op new --tid=[id] --params Name=[name]\n\
74                         add a new target with [id]. [id] must not be zero.\n\
75   --op delete --tid=[id]\n\
76                         delete specific target with [id]. The target must\n\
77                         have no active sessions.\n\
78   --op show --tid=[id]\n\
79                         show target parameters of target with [id].\n\
80   --op show --tid=[id] --sid=[sid]\n\
81                         show iSCSI parameters in effect for session [sid]. If\n\
82                         [sid] is \"0\" (zero), the configured parameters\n\
83                         will be displayed.\n\
84   --op show --tid=[id] --user\n\
85                         show list of Discovery (--tid omitted / id=0 (zero))\n\
86                         or target CHAP accounts.\n\
87   --op show --tid=[id] --user --params=[user]=[name]\n\
88                         show CHAP account information. [user] can be\n\
89                         \"IncomingUser\" or \"OutgoingUser\". If --tid is\n\
90                         omitted / id=0 (zero), [user] is treated as Discovery\n\
91                         user.\n\
92   --op delete --tid=[id] --sid=[sid] --cid=[cid]\n\
93                         delete specific connection with [cid] in a session\n\
94                         with [sid] that the target with [id] has.\n\
95                         If the session has no connections after\n\
96                         the operation, the session will be deleted\n\
97                         automatically.\n\
98   --op update --tid=[id] --params=key1=value1,key2=value2,...\n\
99                         change iSCSI target parameters of specific\n\
100                         target with [id]. You can use parameters in iscsi-scstd.conf\n\
101                         as a key.\n\
102   --op new --tid=[id] --user --params=[user]=[name],Password=[pass]\n\
103                         add a new account with [pass] for specific target.\n\
104                         [user] could be [IncomingUser] or [OutgoingUser].\n\
105                         If you don't specify a target (omit --tid option),\n\
106                         you add a new account for discovery sessions.\n\
107   --op delete --tid=[id] --user --params=[user]=[name]\n\
108                         delete specific account having [name] of specific\n\
109                         target. [user] could be [IncomingUser] or\n\
110                         [OutgoingUser].\n\
111                         If you don't specify a target (omit --tid option),\n\
112                         you delete the account for discovery sessions.\n\
113   --version             display version and exit\n\
114   --help                display this help and exit\n\
115 \n\
116 Report bugs to <iscsitarget-devel@sourceforge.net>.\n");
117         }
118         exit(status == 0 ? 0 : -1);
119 }
120
121 static int str_to_op(char *str)
122 {
123         int op;
124
125         if (!strcmp("new", str))
126                 op = OP_NEW;
127         else if (!strcmp("delete", str))
128                 op = OP_DELETE;
129         else if (!strcmp("update", str))
130                 op = OP_UPDATE;
131         else if (!strcmp("show", str))
132                 op = OP_SHOW;
133         else
134                 op = -1;
135
136         return op;
137 }
138
139 static int iscsid_request_send(int fd, struct iscsi_adm_req *req)
140 {
141         int err, ret;
142
143         do {
144                 ret = write(fd, req, sizeof(*req));
145         } while (ret < 0 && errno == EINTR);
146
147         if (ret != sizeof(*req)) {
148                 err = (ret < 0) ? -errno : -EIO;
149                 fprintf(stderr, "%s failed: written %d, to write %d, "
150                         "error: %s\n", __func__, ret, err, strerror(err));
151         } else
152                 err = 0;
153
154         return err;
155 }
156
157 static int iscsid_response_recv(int fd, struct iscsi_adm_req *req, void *rsp_data,
158                               int rsp_data_sz)
159 {
160         int err, ret;
161         struct iovec iov[2];
162         struct iscsi_adm_rsp rsp;
163
164         iov[0].iov_base = req;
165         iov[0].iov_len = sizeof(*req);
166         iov[1].iov_base = &rsp;
167         iov[1].iov_len = sizeof(rsp);
168
169         do {
170                 ret = readv(fd, iov, 2);
171         } while (ret < 0 && errno == EINTR);
172
173         if (ret != sizeof(rsp) + sizeof(*req)) {
174                 err = (ret < 0) ? -errno : -EIO;
175                 fprintf(stderr, "readv failed: read %d instead of %d (%s)\n",
176                          ret, (int)(sizeof(rsp) + sizeof(*req)), strerror(err));
177         } else
178                 err = rsp.err;
179
180         if (!err && rsp_data_sz && rsp_data) {
181                 ret = read(fd, rsp_data, rsp_data_sz);
182                 if (ret != rsp_data_sz) {
183                         err = (ret < 0) ? -errno : -EIO;
184                         fprintf(stderr, "read failed: read %d instead of %d (%s)\n",
185                                  ret, (int)rsp_data_sz, strerror(err));
186                 }
187         }
188
189         return err;
190 }
191
192 static int iscsid_connect(void)
193 {
194         int fd;
195         struct sockaddr_un addr;
196
197         fd = socket(AF_LOCAL, SOCK_STREAM, 0);
198         if (fd < 0) {
199                 perror("socket() failed");
200                 fd = -errno;
201                 goto out;
202         }
203
204         memset(&addr, 0, sizeof(addr));
205         addr.sun_family = AF_LOCAL;
206         memcpy((char *) &addr.sun_path + 1, ISCSI_ADM_NAMESPACE, strlen(ISCSI_ADM_NAMESPACE));
207
208         if (connect(fd, (struct sockaddr *) &addr, sizeof(addr))) {
209                 fd = -errno;
210                 fprintf(stderr, "Unable to connect to iscsid: %s\n",
211                         strerror(-fd));
212                 goto out;
213         }
214
215 out:
216         return fd;
217 }
218
219 static int iscsid_request(struct iscsi_adm_req *req, void *rsp_data,
220                         int rsp_data_sz)
221 {
222         int fd = -1, err = -EIO;
223
224         if ((fd = iscsid_connect()) < 0) {
225                 err = fd;
226                 goto out_close;
227         }
228
229         if ((err = iscsid_request_send(fd, req)) < 0)
230                 goto out_report;
231
232         err = iscsid_response_recv(fd, req, rsp_data, rsp_data_sz);
233
234 out_report:
235         if (err < 0) {
236                 if (err == -ENOENT)
237                         err = -EINVAL;
238                 fprintf(stderr, "Request to iscsid failed: %s\n", strerror(-err));
239         }
240
241 out_close:
242         if (fd > 0)
243                 close(fd);
244
245         return err;
246 }
247
248 static void show_iscsi_params(int type, struct iscsi_param *param)
249 {
250         int i, nr, len;
251         char buf[1024], *p;
252         struct iscsi_key *keys;
253
254         if (type == key_session) {
255                 nr = session_key_last;
256                 keys = session_keys;
257         } else {
258                 nr = target_key_last;
259                 keys = target_keys;
260         }
261
262         for (i = 0; i < nr; i++) {
263                 memset(buf, 0, sizeof(buf));
264                 strlcpy(buf, keys[i].name, sizeof(buf));
265                 len = strlen(buf);
266                 p = buf + len;
267                 *p++ = '=';
268                 params_val_to_str(keys, i, param[i].val, p, sizeof(buf) - (len + 1));
269                 printf("%s\n", buf);
270         }
271 }
272
273 static int parse_trgt_params(struct msg_trgt *msg, char *params)
274 {
275         char *p, *q;
276
277         while ((p = strsep(&params, ",")) != NULL) {
278                 int idx;
279                 u32 val;
280                 if (!*p)
281                         continue;
282                 if (!(q = strchr(p, '=')))
283                         continue;
284                 *q++ = '\0';
285
286                 if (!((idx = params_index_by_name(p, target_keys)) < 0)) {
287                         if (params_str_to_val(target_keys, idx, q, &val)) {
288                                 fprintf(stderr,
289                                         "Invalid %s value \"%s\".\n",
290                                         target_keys[idx].name, q);
291                                 return -EINVAL;
292                         }
293                         if (!params_check_val(target_keys, idx, &val))
294                                 msg->target_partial |= (1 << idx);
295                         msg->target_params[idx].val = val;
296                         msg->type |= 1 << key_target;
297
298                         continue;
299                 }
300
301                 if (!((idx = params_index_by_name(p, session_keys)) < 0)) {
302                         if (params_str_to_val(session_keys, idx, q, &val)) {
303                                 fprintf(stderr,
304                                         "Invalid %s value \"%s\".\n",
305                                         session_keys[idx].name, q);
306                                 return -EINVAL;
307                         }
308                         if (!params_check_val(session_keys, idx, &val))
309                                 msg->session_partial |= (1 << idx);
310                         msg->session_params[idx].val = val;
311                         msg->type |= 1 << key_session;
312
313                         continue;
314                 }
315                 fprintf(stderr, "Unknown parameter \"%s\".\n", p);
316                 return -EINVAL;
317         }
318
319         return 0;
320 }
321
322 static int trgt_handle(int op, u32 set, u32 tid, char *params)
323 {
324         int err = -EINVAL;
325         struct iscsi_adm_req req;
326
327         if (!(set & SET_TARGET))
328                 goto out;
329
330         memset(&req, 0, sizeof(req));
331         req.tid = tid;
332
333         switch (op) {
334         case OP_NEW:
335         {
336                 char *p = params;
337
338                 if (!params || !(p = strchr(params, '='))) {
339                         fprintf(stderr, "Target name required\n");
340                         err = -EINVAL;
341                         goto out;
342                 }
343                 *p++ = '\0';
344                 if (strcmp(params, "Name")) {
345                         fprintf(stderr, "Target name required\n");
346                         err = -EINVAL;
347                         goto out;
348                 }
349                 req.rcmnd = C_TRGT_NEW;
350                 strlcpy(req.u.trgt.name, p, sizeof(req.u.trgt.name));
351                 break;
352         }
353         case OP_DELETE:
354                 req.rcmnd = C_TRGT_DEL;
355                 break;
356         case OP_UPDATE:
357                 req.rcmnd = C_TRGT_UPDATE;
358                 if ((err = parse_trgt_params(&req.u.trgt, params)) < 0)
359                         goto out;
360                 break;
361         case OP_SHOW:
362                 req.rcmnd = C_TRGT_SHOW;
363                 break;
364         }
365
366         err = iscsid_request(&req, NULL, 0);
367         if (!err && req.rcmnd == C_TRGT_SHOW)
368                 show_iscsi_params(key_target, req.u.trgt.target_params);
369
370 out:
371         return err;
372 }
373
374 static int sess_handle(int op, u32 set, u32 tid, u64 sid, char *params)
375 {
376         int err = -EINVAL;
377         struct iscsi_adm_req req;
378
379         if (op == OP_NEW || op == OP_UPDATE) {
380                 fprintf(stderr, "Unsupported.\n");
381                 goto out;
382         }
383
384         if (!((set & SET_TARGET) && (set & SET_SESSION)))
385                 goto out;
386
387         req.tid = tid;
388         req.sid = sid;
389         req.u.trgt.type = key_session;
390
391         switch (op) {
392         case OP_DELETE:
393                 /* close all connections */
394                 break;
395         case OP_SHOW:
396                 req.rcmnd = C_SESS_SHOW;
397                 err = iscsid_request(&req, NULL, 0);
398                 if (!err)
399                         show_iscsi_params(key_session, req.u.trgt.session_params);
400                 break;
401         }
402
403 out:
404         return err;
405 }
406
407 static int parse_user_params(char *params, u32 *auth_dir, char **user,
408                              char **pass)
409 {
410         char *p, *q;
411
412         while ((p = strsep(&params, ",")) != NULL) {
413                 if (!*p)
414                         continue;
415
416                 if (!(q = strchr(p, '=')))
417                         continue;
418                 *q++ = '\0';
419                 if (isspace(*q))
420                         q++;
421
422                 if (!strcasecmp(p, "IncomingUser")) {
423                         if (*user)
424                                 fprintf(stderr,
425                                         "Already specified IncomingUser %s\n",
426                                         q);
427                         *user = q;
428                         *auth_dir = ISCSI_USER_DIR_INCOMING;
429                 } else if (!strcasecmp(p, "OutgoingUser")) {
430                         if (*user)
431                                 fprintf(stderr,
432                                         "Already specified OutgoingUser %s\n",
433                                         q);
434                         *user = q;
435                         *auth_dir = ISCSI_USER_DIR_OUTGOING;
436                 } else if (!strcasecmp(p, "Password")) {
437                         if (*pass)
438                                 fprintf(stderr,
439                                         "Already specified Password %s\n", q);
440                         *pass = q;
441                 } else {
442                         fprintf(stderr, "Unknown parameter %p\n", q);
443                         return -EINVAL;
444                 }
445         }
446         return 0;
447 }
448
449 static void show_account(int auth_dir, char *user, char *pass)
450 {
451         char buf[(ISCSI_NAME_LEN  + 1) * 2] = {0};
452
453         snprintf(buf, ISCSI_NAME_LEN, "%s", user);
454         if (pass)
455                 snprintf(buf + strlen(buf), ISCSI_NAME_LEN, " %s", pass);
456
457         printf("%sUser %s\n", (auth_dir == ISCSI_USER_DIR_INCOMING) ?
458                "Incoming" : "Outgoing", buf);
459 }
460
461 static int user_handle_show_user(struct iscsi_adm_req *req, char *user)
462 {
463         int err;
464
465         req->rcmnd = C_ACCT_SHOW;
466         strlcpy(req->u.acnt.u.user.name, user, sizeof(req->u.acnt.u.user.name));
467
468         err = iscsid_request(req, NULL, 0);
469         if (!err)
470                 show_account(req->u.acnt.auth_dir, req->u.acnt.u.user.name,
471                              req->u.acnt.u.user.pass);
472
473         return err;
474 }
475
476 static int user_handle_show_list(struct iscsi_adm_req *req)
477 {
478         int i, err, retry;
479         size_t buf_sz = 0;
480         char *buf;
481
482         req->u.acnt.auth_dir = ISCSI_USER_DIR_INCOMING;
483         req->rcmnd = C_ACCT_LIST;
484
485         do {
486                 retry = 0;
487
488                 buf_sz = buf_sz ? buf_sz : ISCSI_NAME_LEN;
489
490                 buf = calloc(buf_sz, sizeof(char *));
491                 if (!buf) {
492                         fprintf(stderr, "Memory allocation failed\n");
493                         return -ENOMEM;
494                 }
495
496                 req->u.acnt.u.list.alloc_len = buf_sz;
497
498                 err = iscsid_request(req, buf, buf_sz);
499                 if (err) {
500                         free(buf);
501                         break;
502                 }
503
504                 if (req->u.acnt.u.list.overflow) {
505                         buf_sz = ISCSI_NAME_LEN * (req->u.acnt.u.list.count +
506                                                    req->u.acnt.u.list.overflow);
507                         retry = 1;
508                         free(buf);
509                         continue;
510                 }
511
512                 for (i = 0; i < req->u.acnt.u.list.count; i++)
513                         show_account(req->u.acnt.auth_dir,
514                                      &buf[i * ISCSI_NAME_LEN], NULL);
515
516                 if (req->u.acnt.auth_dir == ISCSI_USER_DIR_INCOMING) {
517                         req->u.acnt.auth_dir = ISCSI_USER_DIR_OUTGOING;
518                         buf_sz = 0;
519                         retry = 1;
520                 }
521
522                 free(buf);
523
524         } while (retry);
525
526         return err;
527 }
528
529 static int user_handle_show(struct iscsi_adm_req *req, char *user, char *pass)
530 {
531         if (pass)
532                 fprintf(stderr, "Ignoring specified password\n");
533
534         if (user)
535                 return user_handle_show_user(req, user);
536         else
537                 return user_handle_show_list(req);
538 }
539
540 static int user_handle_new(struct iscsi_adm_req *req, char *user, char *pass)
541 {
542         if (!user || !pass) {
543                 fprintf(stderr, "Username and password must be specified\n");
544                 return -EINVAL;
545         }
546
547         req->rcmnd = C_ACCT_NEW;
548
549         strlcpy(req->u.acnt.u.user.name, user, sizeof(req->u.acnt.u.user.name));
550         strlcpy(req->u.acnt.u.user.pass, pass, sizeof(req->u.acnt.u.user.pass));
551
552         return iscsid_request(req, NULL, 0);
553 }
554
555 static int user_handle_del(struct iscsi_adm_req *req, char *user, char *pass)
556 {
557         if (!user) {
558                 fprintf(stderr, "Username must be specified\n");
559                 return -EINVAL;
560         }
561         
562         if (pass)
563                 fprintf(stderr, "Ignoring specified password\n");
564
565         req->rcmnd = C_ACCT_DEL;
566
567         strlcpy(req->u.acnt.u.user.name, user, sizeof(req->u.acnt.u.user.name));
568
569         return iscsid_request(req, NULL, 0);
570 }
571
572 static int user_handle(int op, u32 set, u32 tid, char *params)
573 {
574         int err = -EINVAL;
575         char *user = NULL, *pass = NULL;
576         struct iscsi_adm_req req;
577         static user_handle_fn_t *user_handle_fn[] = {
578                 user_handle_new,
579                 user_handle_del,
580                 NULL,
581                 user_handle_show,
582         }, *fn;
583
584         if (set & ~(SET_TARGET | SET_USER))
585                 goto out;
586
587         memset(&req, 0, sizeof(req));
588         req.tid = tid;
589
590         err = parse_user_params(params, &req.u.acnt.auth_dir, &user, &pass);
591         if (err)
592                 goto out;
593
594         if ((op >= sizeof(user_handle_fn)/sizeof(user_handle_fn[0])) ||
595             ((fn = user_handle_fn[op]) == NULL)) {
596                 fprintf(stderr, "Unsupported\n");
597                 goto out;
598         }
599
600         err = fn(&req, user, pass);
601
602 out:
603         return err;
604 }
605
606 static int conn_handle(int op, u32 set, u32 tid, u64 sid, u32 cid, char *params)
607 {
608         int err = -EINVAL;
609         struct iscsi_adm_req req;
610
611         if (op == OP_NEW || op == OP_UPDATE) {
612                 fprintf(stderr, "Unsupported.\n");
613                 goto out;
614         }
615
616         if (!((set & SET_TARGET) && (set & SET_SESSION) && (set & SET_CONNECTION)))
617                 goto out;
618
619         memset(&req, 0, sizeof(req));
620         req.tid = tid;
621         req.sid = sid;
622         req.cid = cid;
623
624         switch (op) {
625         case OP_DELETE:
626                 req.rcmnd = C_CONN_DEL;
627                 break;
628         case OP_SHOW:
629                 req.rcmnd = C_CONN_SHOW;
630                 /* TODO */
631                 break;
632         }
633
634         err = iscsid_request(&req, NULL, 0);
635 out:
636         return err;
637 }
638
639 int main(int argc, char **argv)
640 {
641         int ch, longindex;
642         int err = -EINVAL, op = -1;
643         u32 tid = 0, cid = 0, set = 0;
644         u64 sid = 0;
645         char *params = NULL;
646
647         while ((ch = getopt_long(argc, argv, "o:t:s:c:l:p:uvh",
648                                  long_options, &longindex)) >= 0) {
649                 switch (ch) {
650                 case 'o':
651                         op = str_to_op(optarg);
652                         break;
653                 case 't':
654                         tid = strtoul(optarg, NULL, 0);
655                         set |= SET_TARGET;
656                         break;
657                 case 's':
658                         sid = strtoull(optarg, NULL, 0);
659                         set |= SET_SESSION;
660                         break;
661                 case 'c':
662                         cid = strtoul(optarg, NULL, 0);
663                         set |= SET_CONNECTION;
664                         break;
665                 case 'p':
666                         params = optarg;
667                         break;
668                 case 'u':
669                         set |= SET_USER;
670                         break;
671                 case 'v':
672                         printf("%s version %s\n", program_name, ISCSI_VERSION_STRING);
673                         exit(0);
674                         break;
675                 case 'h':
676                         usage(0);
677                         break;
678                 default:
679                         usage(-1);
680                 }
681         }
682
683         if (op < 0) {
684                 fprintf(stderr, "You must specify the operation type\n");
685                 goto out;
686         }
687
688         if (optind < argc) {
689                 fprintf(stderr, "unrecognized: ");
690                 while (optind < argc)
691                         fprintf(stderr, "%s", argv[optind++]);
692                 fprintf(stderr, "\n");
693                 usage(-1);
694         }
695
696         if (set & SET_USER)
697                 err = user_handle(op, set, tid, params);
698         else if (set & SET_CONNECTION)
699                 err = conn_handle(op, set, tid, sid, cid, params);
700         else if (set & SET_SESSION)
701                 err = sess_handle(op, set, tid, sid, params);
702         else if (set & SET_TARGET)
703                 err = trgt_handle(op, set, tid, params);
704         else
705                 usage(-1);
706
707 out:
708         return err;
709 }