bcaca806aa27705a22f6a9f14ddec895eb4b01d3
[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_param(int type, struct iscsi_param *param)
249 {
250         int i, nr;
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                 strcpy(buf, keys[i].name);
265                 p = buf + strlen(buf);
266                 *p++ = '=';
267                 param_val_to_str(keys, i, param[i].val, p);
268                 printf("%s\n", buf);
269         }
270 }
271
272 static int parse_trgt_params(struct msg_trgt *msg, char *params)
273 {
274         char *p, *q;
275
276         while ((p = strsep(&params, ",")) != NULL) {
277                 int idx;
278                 u32 val;
279                 if (!*p)
280                         continue;
281                 if (!(q = strchr(p, '=')))
282                         continue;
283                 *q++ = '\0';
284
285                 if (!((idx = param_index_by_name(p, target_keys)) < 0)) {
286                         if (param_str_to_val(target_keys, idx, q, &val)) {
287                                 fprintf(stderr,
288                                         "Invalid %s value \"%s\".\n",
289                                         target_keys[idx].name, q);
290                                 return -EINVAL;
291                         }
292                         if (!param_check_val(target_keys, idx, &val))
293                                 msg->target_partial |= (1 << idx);
294                         msg->target_param[idx].val = val;
295                         msg->type |= 1 << key_target;
296
297                         continue;
298                 }
299
300                 if (!((idx = param_index_by_name(p, session_keys)) < 0)) {
301                         if (param_str_to_val(session_keys, idx, q, &val)) {
302                                 fprintf(stderr,
303                                         "Invalid %s value \"%s\".\n",
304                                         session_keys[idx].name, q);
305                                 return -EINVAL;
306                         }
307                         if (!param_check_val(session_keys, idx, &val))
308                                 msg->session_partial |= (1 << idx);
309                         msg->session_param[idx].val = val;
310                         msg->type |= 1 << key_session;
311
312                         continue;
313                 }
314                 fprintf(stderr, "Unknown parameter \"%s\".\n", p);
315                 return -EINVAL;
316         }
317
318         return 0;
319 }
320
321 static int trgt_handle(int op, u32 set, u32 tid, char *params)
322 {
323         int err = -EINVAL;
324         struct iscsi_adm_req req;
325
326         if (!(set & SET_TARGET))
327                 goto out;
328
329         memset(&req, 0, sizeof(req));
330         req.tid = tid;
331
332         switch (op) {
333         case OP_NEW:
334         {
335                 char *p = params;
336
337                 if (!params || !(p = strchr(params, '='))) {
338                         fprintf(stderr, "Target name required\n");
339                         err = -EINVAL;
340                         goto out;
341                 }
342                 *p++ = '\0';
343                 if (strcmp(params, "Name")) {
344                         fprintf(stderr, "Target name required\n");
345                         err = -EINVAL;
346                         goto out;
347                 }
348                 req.rcmnd = C_TRGT_NEW;
349                 strncpy(req.u.trgt.name, p, sizeof(req.u.trgt.name) - 1);
350                 break;
351         }
352         case OP_DELETE:
353                 req.rcmnd = C_TRGT_DEL;
354                 break;
355         case OP_UPDATE:
356                 req.rcmnd = C_TRGT_UPDATE;
357                 if ((err = parse_trgt_params(&req.u.trgt, params)) < 0)
358                         goto out;
359                 break;
360         case OP_SHOW:
361                 req.rcmnd = C_TRGT_SHOW;
362                 break;
363         }
364
365         err = iscsid_request(&req, NULL, 0);
366         if (!err && req.rcmnd == C_TRGT_SHOW)
367                 show_iscsi_param(key_target, req.u.trgt.target_param);
368
369 out:
370         return err;
371 }
372
373 static int sess_handle(int op, u32 set, u32 tid, u64 sid, char *params)
374 {
375         int err = -EINVAL;
376         struct iscsi_adm_req req;
377
378         if (op == OP_NEW || op == OP_UPDATE) {
379                 fprintf(stderr, "Unsupported.\n");
380                 goto out;
381         }
382
383         if (!((set & SET_TARGET) && (set & SET_SESSION)))
384                 goto out;
385
386         req.tid = tid;
387         req.sid = sid;
388         req.u.trgt.type = key_session;
389
390         switch (op) {
391         case OP_DELETE:
392                 /* close all connections */
393                 break;
394         case OP_SHOW:
395                 req.rcmnd = C_SESS_SHOW;
396                 err = iscsid_request(&req, NULL, 0);
397                 if (!err)
398                         show_iscsi_param(key_session, req.u.trgt.session_param);
399                 break;
400         }
401
402 out:
403         return err;
404 }
405
406 static int parse_user_params(char *params, u32 *auth_dir, char **user,
407                              char **pass)
408 {
409         char *p, *q;
410
411         while ((p = strsep(&params, ",")) != NULL) {
412                 if (!*p)
413                         continue;
414
415                 if (!(q = strchr(p, '=')))
416                         continue;
417                 *q++ = '\0';
418                 if (isspace(*q))
419                         q++;
420
421                 if (!strcasecmp(p, "IncomingUser")) {
422                         if (*user)
423                                 fprintf(stderr,
424                                         "Already specified IncomingUser %s\n",
425                                         q);
426                         *user = q;
427                         *auth_dir = AUTH_DIR_INCOMING;
428                 } else if (!strcasecmp(p, "OutgoingUser")) {
429                         if (*user)
430                                 fprintf(stderr,
431                                         "Already specified OutgoingUser %s\n",
432                                         q);
433                         *user = q;
434                         *auth_dir = AUTH_DIR_OUTGOING;
435                 } else if (!strcasecmp(p, "Password")) {
436                         if (*pass)
437                                 fprintf(stderr,
438                                         "Already specified Password %s\n", q);
439                         *pass = q;
440                 } else {
441                         fprintf(stderr, "Unknown parameter %p\n", q);
442                         return -EINVAL;
443                 }
444         }
445         return 0;
446 }
447
448 static void show_account(int auth_dir, char *user, char *pass)
449 {
450         char buf[(ISCSI_NAME_LEN  + 1) * 2] = {0};
451
452         snprintf(buf, ISCSI_NAME_LEN, "%s", user);
453         if (pass)
454                 snprintf(buf + strlen(buf), ISCSI_NAME_LEN, " %s", pass);
455
456         printf("%sUser %s\n", (auth_dir == AUTH_DIR_INCOMING) ?
457                "Incoming" : "Outgoing", buf);
458 }
459
460 static int user_handle_show_user(struct iscsi_adm_req *req, char *user)
461 {
462         int err;
463
464         req->rcmnd = C_ACCT_SHOW;
465         strncpy(req->u.acnt.u.user.name, user,
466                 sizeof(req->u.acnt.u.user.name) - 1);
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 = AUTH_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 == AUTH_DIR_INCOMING) {
517                         req->u.acnt.auth_dir = AUTH_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         strncpy(req->u.acnt.u.user.name, user,
550                 sizeof(req->u.acnt.u.user.name) - 1);
551         strncpy(req->u.acnt.u.user.pass, pass,
552                 sizeof(req->u.acnt.u.user.pass) - 1);
553
554         return iscsid_request(req, NULL, 0);
555 }
556
557 static int user_handle_del(struct iscsi_adm_req *req, char *user, char *pass)
558 {
559         if (!user) {
560                 fprintf(stderr, "Username must be specified\n");
561                 return -EINVAL;
562         }
563         
564         if (pass)
565                 fprintf(stderr, "Ignoring specified password\n");
566
567         req->rcmnd = C_ACCT_DEL;
568
569         strncpy(req->u.acnt.u.user.name, user,
570                 sizeof(req->u.acnt.u.user.name) - 1);
571
572         return iscsid_request(req, NULL, 0);
573 }
574
575 static int user_handle(int op, u32 set, u32 tid, char *params)
576 {
577         int err = -EINVAL;
578         char *user = NULL, *pass = NULL;
579         struct iscsi_adm_req req;
580         static user_handle_fn_t *user_handle_fn[] = {
581                 user_handle_new,
582                 user_handle_del,
583                 NULL,
584                 user_handle_show,
585         }, *fn;
586
587         if (set & ~(SET_TARGET | SET_USER))
588                 goto out;
589
590         memset(&req, 0, sizeof(req));
591         req.tid = tid;
592
593         err = parse_user_params(params, &req.u.acnt.auth_dir, &user, &pass);
594         if (err)
595                 goto out;
596
597         if ((op >= sizeof(user_handle_fn)/sizeof(user_handle_fn[0])) ||
598             ((fn = user_handle_fn[op]) == NULL)) {
599                 fprintf(stderr, "Unsupported\n");
600                 goto out;
601         }
602
603         err = fn(&req, user, pass);
604
605 out:
606         return err;
607 }
608
609 static int conn_handle(int op, u32 set, u32 tid, u64 sid, u32 cid, char *params)
610 {
611         int err = -EINVAL;
612         struct iscsi_adm_req req;
613
614         if (op == OP_NEW || op == OP_UPDATE) {
615                 fprintf(stderr, "Unsupported.\n");
616                 goto out;
617         }
618
619         if (!((set & SET_TARGET) && (set & SET_SESSION) && (set & SET_CONNECTION)))
620                 goto out;
621
622         memset(&req, 0, sizeof(req));
623         req.tid = tid;
624         req.sid = sid;
625         req.cid = cid;
626
627         switch (op) {
628         case OP_DELETE:
629                 req.rcmnd = C_CONN_DEL;
630                 break;
631         case OP_SHOW:
632                 req.rcmnd = C_CONN_SHOW;
633                 /* TODO */
634                 break;
635         }
636
637         err = iscsid_request(&req, NULL, 0);
638 out:
639         return err;
640 }
641
642 int main(int argc, char **argv)
643 {
644         int ch, longindex;
645         int err = -EINVAL, op = -1;
646         u32 tid = 0, cid = 0, set = 0;
647         u64 sid = 0;
648         char *params = NULL;
649
650         while ((ch = getopt_long(argc, argv, "o:t:s:c:l:p:uvh",
651                                  long_options, &longindex)) >= 0) {
652                 switch (ch) {
653                 case 'o':
654                         op = str_to_op(optarg);
655                         break;
656                 case 't':
657                         tid = strtoul(optarg, NULL, 0);
658                         set |= SET_TARGET;
659                         break;
660                 case 's':
661                         sid = strtoull(optarg, NULL, 0);
662                         set |= SET_SESSION;
663                         break;
664                 case 'c':
665                         cid = strtoul(optarg, NULL, 0);
666                         set |= SET_CONNECTION;
667                         break;
668                 case 'p':
669                         params = optarg;
670                         break;
671                 case 'u':
672                         set |= SET_USER;
673                         break;
674                 case 'v':
675                         printf("%s version %s\n", program_name, ISCSI_VERSION_STRING);
676                         exit(0);
677                         break;
678                 case 'h':
679                         usage(0);
680                         break;
681                 default:
682                         usage(-1);
683                 }
684         }
685
686         if (op < 0) {
687                 fprintf(stderr, "You must specify the operation type\n");
688                 goto out;
689         }
690
691         if (optind < argc) {
692                 fprintf(stderr, "unrecognized: ");
693                 while (optind < argc)
694                         fprintf(stderr, "%s", argv[optind++]);
695                 fprintf(stderr, "\n");
696                 usage(-1);
697         }
698
699         if (set & SET_USER)
700                 err = user_handle(op, set, tid, params);
701         else if (set & SET_CONNECTION)
702                 err = conn_handle(op, set, tid, sid, cid, params);
703         else if (set & SET_SESSION)
704                 err = sess_handle(op, set, tid, sid, params);
705         else if (set & SET_TARGET)
706                 err = trgt_handle(op, set, tid, params);
707         else
708                 usage(-1);
709
710 out:
711         return err;
712 }