No empty .Rs/.Re
[netbsd-mini2440.git] / external / bsd / iscsi / dist / src / lib / util.c
blobd89e8e81d9d4e6fbd595a3e54847ecb9df933afb
1 /*
2 * IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
3 * By downloading, copying, installing or using the software you agree
4 * to this license. If you do not agree to this license, do not
5 * download, install, copy or use the software.
7 * Intel License Agreement
9 * Copyright (c) 2000, Intel Corporation
10 * All rights reserved.
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
16 * -Redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer.
19 * -Redistributions in binary form must reproduce the above copyright
20 * notice, this list of conditions and the following disclaimer in the
21 * documentation and/or other materials provided with the
22 * distribution.
24 * -The name of Intel Corporation may not be used to endorse or
25 * promote products derived from this software without specific prior
26 * written permission.
28 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
29 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
30 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
31 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL
32 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
33 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
34 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
35 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
36 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
37 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
38 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
39 * SUCH DAMAGE.
41 #include "config.h"
43 #include <sys/types.h>
44 #include <sys/stat.h>
46 #ifdef HAVE_SYS_SOCKET_H
47 #include <sys/socket.h>
48 #endif
50 #ifdef HAVE_SYS_UIO_H
51 #include <sys/uio.h>
52 #endif
54 #ifdef HAVE_ARPA_INET_H
55 #include <arpa/inet.h>
56 #endif
58 #ifdef HAVE_NETINET_IN_H
59 #include <netinet/in.h>
60 #endif
62 #ifdef HAVE_NETINET_TCP_H
63 #include <netinet/tcp.h>
64 #endif
66 #ifdef HAVE_NETDB_H
67 #include <netdb.h>
68 #endif
70 #ifdef HAVE_CTYPE_H
71 #include <ctype.h>
72 #endif
74 #ifdef HAVE_ERRNO_H
75 #include <errno.h>
76 #endif
78 #ifdef HAVE_PTHREAD_H
79 #include <pthread.h>
80 #endif
82 #ifdef HAVE_STDARG_H
83 #include <stdarg.h>
84 #endif
86 #ifdef HAVE_SYS_SELECT_H
87 #include <sys/select.h>
88 #endif
90 #ifdef HAVE_POLL_H
91 #include <poll.h>
92 #endif
94 #include <stdio.h>
95 #include <stdlib.h>
97 #ifdef HAVE_STRING_H
98 #include <string.h>
99 #endif
101 #include <unistd.h>
103 #include "compat.h"
105 #define EXTERN
106 #include "iscsiutil.h"
111 * Memory Allocation
114 void *
115 iscsi_malloc_atomic(unsigned n)
117 void *ptr;
119 ptr = malloc(n);
120 iscsi_trace(TRACE_MEM, "iscsi_malloc_atomic(%u) = %p\n", n, ptr);
121 return ptr;
124 void *
125 iscsi_malloc(unsigned n)
127 void *ptr;
129 ptr = malloc(n);
130 iscsi_trace(TRACE_MEM, "iscsi_malloc(%u) = %p\n", n, ptr);
131 return ptr;
134 void
135 iscsi_free_atomic(void *ptr)
137 (void) free(ptr);
138 iscsi_trace(TRACE_MEM, "iscsi_free_atomic(%p)\n", ptr);
141 void
142 iscsi_free(void *ptr)
144 (void) free(ptr);
145 iscsi_trace(TRACE_MEM, "iscsi_free(%p)\n", ptr);
148 /* debugging levels */
149 void
150 set_debug(const char *level)
152 if (strcmp(level, "net") == 0) {
153 iscsi_debug_level |= TRACE_NET_ALL;
154 } else if (strcmp(level, "iscsi") == 0) {
155 iscsi_debug_level |= TRACE_ISCSI_ALL;
156 } else if (strcmp(level, "scsi") == 0) {
157 iscsi_debug_level |= TRACE_SCSI_ALL;
158 } else if (strcmp(level, "osd") == 0) {
159 iscsi_debug_level |= TRACE_OSD;
160 } else if (strcmp(level, "all") == 0) {
161 iscsi_debug_level |= TRACE_ALL;
166 * Threading Routines
169 iscsi_thread_create(iscsi_thread_t * thread, void *(*proc) (void *), void *arg)
171 if (pthread_create(&thread->pthread, NULL, proc, arg) != 0) {
172 iscsi_err(__FILE__, __LINE__, "pthread_create() failed\n");
173 return -1;
175 if (pthread_detach(thread->pthread) != 0) {
176 iscsi_err(__FILE__, __LINE__, "pthread_detach() failed\n");
177 return -1;
179 return 0;
183 * Queuing Functions
185 int
186 iscsi_queue_init(iscsi_queue_t * q, int depth)
188 q->head = q->tail = q->count = 0;
189 q->depth = depth;
190 q->elem = iscsi_malloc_atomic((unsigned)(depth * sizeof(void *)));
191 if (q->elem == NULL) {
192 iscsi_err(__FILE__, __LINE__, "iscsi_malloc_atomic() failed\n");
193 return -1;
195 iscsi_spin_init(&q->lock);
196 return 0;
199 void
200 iscsi_queue_destroy(iscsi_queue_t * q)
202 iscsi_free_atomic(q->elem);
205 int
206 iscsi_queue_full(iscsi_queue_t * q)
208 return (q->count == q->depth);
211 int
212 iscsi_queue_depth(iscsi_queue_t * q)
214 return q->count;
217 int
218 iscsi_queue_insert(iscsi_queue_t * q, void *ptr)
220 uint32_t flags;
222 iscsi_spin_lock_irqsave(&q->lock, &flags);
223 if (iscsi_queue_full(q)) {
224 iscsi_err(__FILE__, __LINE__, "QUEUE FULL\n");
225 iscsi_spin_unlock_irqrestore(&q->lock, &flags);
226 return -1;
228 q->elem[q->tail] = ptr;
229 q->tail++;
230 if (q->tail == q->depth) {
231 q->tail = 0;
233 q->count++;
234 iscsi_spin_unlock_irqrestore(&q->lock, &flags);
235 return 0;
238 void *
239 iscsi_queue_remove(iscsi_queue_t *q)
241 uint32_t flags = 0;
242 void *ptr;
244 iscsi_spin_lock_irqsave(&q->lock, &flags);
245 if (!iscsi_queue_depth(q)) {
246 iscsi_trace(TRACE_QUEUE, "QUEUE EMPTY\n");
247 iscsi_spin_unlock_irqrestore(&q->lock, &flags);
248 return NULL;
250 q->count--;
251 ptr = q->elem[q->head];
252 q->head++;
253 if (q->head == q->depth) {
254 q->head = 0;
256 iscsi_spin_unlock_irqrestore(&q->lock, &flags);
257 return ptr;
260 void
261 iscsi_trace(const int trace, const char *fmt, ...)
263 #ifdef CONFIG_ISCSI_DEBUG
264 va_list vp;
265 char buf[8192];
267 if (iscsi_debug_level & trace) {
268 va_start(vp, fmt);
269 (void) vsnprintf(buf, sizeof(buf), fmt, vp);
270 printf("pid %d: %s", (int) getpid(), buf);
271 va_end(vp);
273 #endif
276 void
277 iscsi_warn(const char *f, const int line, const char *fmt, ...)
279 #ifdef CONFIG_ISCSI_DEBUG
280 va_list vp;
281 char buf[8192];
283 if (iscsi_debug_level & TRACE_WARN) {
284 va_start(vp, fmt);
285 (void) vsnprintf(buf, sizeof(buf), fmt, vp);
286 printf("pid %d:%s:%d: ***WARNING*** %s",
287 (int) getpid(), f, line,
288 buf);
289 va_end(vp);
291 #endif
294 void
295 iscsi_err(const char *f, const int line, const char *fmt, ...)
297 #ifdef CONFIG_ISCSI_DEBUG
298 va_list vp;
299 char buf[8192];
301 va_start(vp, fmt);
302 (void) vsnprintf(buf, sizeof(buf), fmt, vp);
303 va_end(vp);
304 printf("pid %d:%s:%d: ***ERROR*** %s", (int) getpid(), f, line, buf);
305 # ifdef HAVE_SYSLOG
306 syslog(LOG_ERR, "pid %d:%s:%d: ***ERROR*** %s", getpid(), f, line, buf);
307 # endif /* HAVE_SYSLOG */
308 #endif
311 void
312 iscsi_print_buffer(const char *buf, const size_t len)
314 #ifdef CONFIG_ISCSI_DEBUG
315 size_t i;
317 if (iscsi_debug_level & TRACE_NET_BUFF) {
318 for (i=0 ; i < len; i++) {
319 if (i % 4 == 0) {
320 if (i) {
321 printf("\n");
323 printf("%4zu:", i);
325 printf("%2x ", (uint8_t) (buf)[i]);
327 if ((len + 1) % 32) {
328 printf("\n");
331 #endif
335 * Hashing Functions
337 #include "initiator.h"
339 int
340 hash_init(hash_t * h, int n)
342 int i;
344 iscsi_spin_init(&h->lock);
345 h->n = n;
346 h->insertions = 0;
347 h->collisions = 0;
348 h->bucket = iscsi_malloc_atomic(n * sizeof(initiator_cmd_t *));
349 if (h->bucket == NULL) {
350 iscsi_err(__FILE__, __LINE__, "iscsi_malloc_atomic() failed\n");
351 return -1;
353 for (i = 0; i < n; i++)
354 h->bucket[i] = NULL;
355 return 0;
358 int
359 hash_insert(hash_t * h, initiator_cmd_t * cmd, unsigned key)
361 int i;
363 iscsi_spin_lock(&h->lock);
364 cmd->hash_next = NULL;
365 cmd->key = key;
367 i = key % (h->n);
368 if (h->bucket[i] == NULL) {
369 iscsi_trace(TRACE_HASH,
370 "inserting key %u (val 0x%p) into bucket[%d]\n",
371 key, cmd, i);
372 h->bucket[i] = cmd;
373 } else {
374 cmd->hash_next = h->bucket[i];
375 h->bucket[i] = cmd;
376 h->collisions++;
377 iscsi_trace(TRACE_HASH,
378 "inserting key %u (val 0x%p) into bucket[%d] "
379 "(collision)\n", key, cmd, i);
381 h->insertions++;
382 iscsi_spin_unlock(&h->lock);
383 return 0;
386 struct initiator_cmd_t *
387 hash_remove(hash_t * h, unsigned key)
389 initiator_cmd_t *prev;
390 initiator_cmd_t *curr;
391 int i;
393 iscsi_spin_lock(&h->lock);
394 i = key % (h->n);
395 if (h->bucket[i] == NULL) {
396 iscsi_err(__FILE__, __LINE__, "bucket emtpy\n");
397 curr = NULL;
398 } else {
399 prev = NULL;
400 curr = h->bucket[i];
401 while ((curr->key != key) && (curr->hash_next != NULL)) {
402 prev = curr;
403 curr = curr->hash_next;
405 if (curr->key != key) {
406 iscsi_err(__FILE__, __LINE__,
407 "key %u (%#x) not found in bucket[%d]\n",
408 key, key, i);
409 curr = NULL;
410 } else {
411 if (prev == NULL) {
412 h->bucket[i] = h->bucket[i]->hash_next;
413 iscsi_trace(TRACE_HASH,
414 "removed key %u (val 0x%p) from head "
415 "of bucket\n", key, curr);
416 } else {
417 prev->hash_next = curr->hash_next;
418 if (prev->hash_next == NULL) {
419 iscsi_trace(TRACE_HASH,
420 "removed key %u (val 0x%p) "
421 "from end of bucket\n", key,
422 curr);
423 } else {
424 iscsi_trace(TRACE_HASH,
425 "removed key %u (val 0x%p) "
426 "from middle of bucket\n",
427 key, curr);
432 iscsi_spin_unlock(&h->lock);
433 return curr;
436 int
437 hash_destroy(hash_t * h)
439 iscsi_free_atomic(h->bucket);
440 return 0;
444 * Socket Functions
447 int
448 modify_iov(struct iovec ** iov_ptr, int *iovc, uint32_t offset, uint32_t length)
450 size_t len;
451 int disp = offset;
452 int i;
453 struct iovec *iov = *iov_ptr;
454 char *basep;
456 /* Given <offset>, find beginning iovec and modify its base
457 * and length */
458 len = 0;
459 for (i = 0; i < *iovc; i++) {
460 len += iov[i].iov_len;
461 if (len > offset) {
462 iscsi_trace(TRACE_NET_IOV,
463 "found offset %u in iov[%d]\n", offset, i);
464 break;
466 disp -= iov[i].iov_len;
468 if (i == *iovc) {
469 iscsi_err(__FILE__, __LINE__,
470 "sum of iov lens (%u) < offset (%u)\n", len, offset);
471 return -1;
473 iov[i].iov_len -= disp;
474 basep = iov[i].iov_base;
475 basep += disp;
476 iov[i].iov_base = basep;
477 *iovc -= i;
478 *iov_ptr = &(iov[i]);
479 iov = *iov_ptr;
482 * Given <length>, find ending iovec and modify its length (base does
483 * not change)
485 len = 0; /* we should re-use len and i here... */
486 for (i = 0; i < *iovc; i++) {
487 len += iov[i].iov_len;
488 if (len >= length) {
489 iscsi_trace(TRACE_NET_IOV,
490 "length %u ends in iovec[%d]\n", length, i);
491 break;
494 if (i == *iovc) {
495 iscsi_err(__FILE__, __LINE__,
496 "sum of iovec lens (%u) < length (%u)\n", len, length);
497 for (i = 0; i < *iovc; i++) {
498 iscsi_err(__FILE__, __LINE__,
499 "iov[%d].iov_base = %p (len %u)\n",
500 i, iov[i].iov_base, (unsigned)iov[i].iov_len);
502 return -1;
504 iov[i].iov_len -= (len - length);
505 *iovc = i + 1;
507 #ifdef CONFIG_ISCSI_DEBUG
508 iscsi_trace(TRACE_NET_IOV, "new iov:\n");
509 len = 0;
510 for (i = 0; i < *iovc; i++) {
511 iscsi_trace(TRACE_NET_IOV, "iov[%d].iov_base = %p (len %u)\n",
512 i, iov[i].iov_base, (unsigned)iov[i].iov_len);
513 len += iov[i].iov_len;
515 iscsi_trace(TRACE_NET_IOV, "new iov length: %u bytes\n", len);
516 #endif
518 return 0;
521 int
522 iscsi_sock_setsockopt(int * sock, int level, int optname, void *optval,
523 unsigned optlen)
525 int rc;
527 if ((rc = setsockopt(*sock, level, optname, optval, optlen)) != 0) {
528 iscsi_err(__FILE__, __LINE__,
529 "sock->ops->setsockopt() failed: rc %d errno %d\n",
530 rc, errno);
531 return 0;
533 return 1;
536 int
537 iscsi_sock_getsockopt(int * sock, int level, int optname, void *optval, unsigned *optlen)
539 int rc;
541 if ((rc = getsockopt(*sock, level, optname, optval, optlen)) != 0) {
542 iscsi_err(__FILE__, __LINE__,
543 "sock->ops->getsockopt() failed: rc %d errno %d\n",
544 rc, errno);
545 return 0;
547 return 1;
550 int
551 iscsi_sock_create(int * sock)
553 int rc;
555 if ((*sock = rc = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
556 iscsi_err(__FILE__, __LINE__,
557 "socket() failed: rc %d errno %d\n", rc, errno);
558 return 0;
560 return 1;
563 int
564 iscsi_sock_bind(int sock, int port)
566 struct sockaddr_in laddr;
567 int rc;
569 (void) memset(&laddr, 0x0, sizeof(laddr));
570 laddr.sin_family = AF_INET;
571 laddr.sin_addr.s_addr = INADDR_ANY;
572 laddr.sin_port = ISCSI_HTONS(port);
573 rc = bind(sock, (struct sockaddr *) (void *) &laddr, sizeof(laddr));
574 if (rc < 0) {
575 iscsi_err(__FILE__, __LINE__,
576 "bind() failed: rc %d errno %d\n", rc, errno);
577 return 0;
579 return 1;
582 int
583 iscsi_sock_listen(int sock)
585 int rc;
587 if ((rc = listen(sock, 32)) < 0) {
588 iscsi_err(__FILE__, __LINE__,
589 "listen() failed: rc %d errno %d\n", rc, errno);
590 return 0;
592 return 1;
595 #ifndef ISCSI_MAXSOCK
596 #define ISCSI_MAXSOCK 8
597 #endif
600 iscsi_socks_establish(int *sockv, int *famv, int *sockc, char *family, int port)
602 struct addrinfo hints;
603 struct addrinfo *res;
604 struct addrinfo *res0;
605 const char *cause = NULL;
606 char portnum[31];
607 int one = 1;
608 int error;
610 (void) memset(&hints, 0x0, sizeof(hints));
611 hints.ai_family = (strcmp(family, "unspec") == 0) ? PF_UNSPEC :
612 (strcmp(family, "4") == 0) ? AF_INET : AF_INET6;
613 hints.ai_socktype = SOCK_STREAM;
614 hints.ai_flags = AI_PASSIVE;
615 #ifdef AI_NUMERICSERV
616 hints.ai_flags |= AI_NUMERICSERV;
617 #endif
618 (void) snprintf(portnum, sizeof(portnum), "%d", port);
619 if ((error = getaddrinfo(NULL, portnum, &hints, &res0)) != 0) {
620 hints.ai_flags = AI_PASSIVE;
621 if ((error = getaddrinfo(NULL, "iscsi-target", &hints,
622 &res0)) != 0 ||
623 (error = getaddrinfo(NULL, "iscsi", &hints, &res0)) != 0) {
624 iscsi_err(__FILE__, __LINE__, "getaddrinfo: %s",
625 gai_strerror(error));
626 return 0;
629 *sockc = 0;
630 for (res = res0; res && *sockc < ISCSI_MAXSOCK; res = res->ai_next) {
631 sockv[*sockc] = socket(res->ai_family, res->ai_socktype,
632 res->ai_protocol);
633 if (sockv[*sockc] < 0) {
634 cause = "socket";
635 continue;
637 famv[*sockc] = res->ai_family;
638 if (!iscsi_sock_setsockopt(&sockv[*sockc], SOL_SOCKET,
639 SO_REUSEADDR, &one, sizeof(one))) {
640 iscsi_err(__FILE__, __LINE__,
641 "iscsi_sock_setsockopt() failed\n");
642 continue;
644 if (!iscsi_sock_setsockopt(&sockv[*sockc], SOL_TCP,
645 TCP_NODELAY, &one, sizeof(one))) {
646 iscsi_err(__FILE__, __LINE__,
647 "iscsi_sock_setsockopt() failed\n");
648 continue;
651 if (bind(sockv[*sockc], res->ai_addr, res->ai_addrlen) < 0) {
652 cause = "bind";
653 close(sockv[*sockc]);
654 continue;
656 (void) listen(sockv[*sockc], 32);
657 *sockc += 1;
659 if (*sockc == 0) {
660 iscsi_err(__FILE__, __LINE__,
661 "iscsi_sock_establish: no sockets found: %s", cause);
662 freeaddrinfo(res0);
663 return 0;
665 freeaddrinfo(res0);
666 return 1;
669 /* return the address family for the socket */
670 const char *
671 iscsi_address_family(int fam)
673 switch(fam) {
674 case 4:
675 return "IPv4";
676 case 6:
677 return "IPv6";
678 default:
679 return "[unknown type]";
683 /* wait for a connection to come in on a socket */
684 /* ARGSUSED2 */
686 iscsi_waitfor_connection(int *sockv, int sockc, const char *cf, int *sock)
688 #ifdef HAVE_POLL
689 struct pollfd socks[ISCSI_MAXSOCK];
690 int i;
692 for (;;) {
693 for (i = 0 ; i < sockc ; i++) {
694 socks[i].fd = sockv[i];
695 socks[i].events = POLLIN;
696 socks[i].revents = 0;
698 switch(poll(socks, (unsigned)sockc, INFTIM)) {
699 case -1:
700 /* interrupted system call */
701 continue;
702 case 0:
703 /* timeout */
704 continue;
705 default:
706 for (i = 0 ; i < sockc ; i++) {
707 if (socks[i].revents & POLLIN) {
708 iscsi_trace(TRACE_NET_DEBUG,
709 "connection %d selected\n",
710 sockv[i]);
711 *sock = sockv[i];
712 return i;
717 #else
718 fd_set infds;
719 int i;
721 for (;;) {
722 FD_ZERO(&infds);
723 for (i = 0 ; i < sockc ; i++) {
724 FD_SET(sockv[i], &infds);
726 iscsi_trace(TRACE_NET_DEBUG, "waiting for connection\n");
727 switch (select(32, &infds, NULL, NULL, NULL)) {
728 case -1:
729 /* interrupted system call */
730 continue;
731 case 0:
732 /* timeout */
733 continue;
734 default:
735 for (i = 0 ; i < sockc ; i++) {
736 if (FD_ISSET(sockv[i], &infds)) {
737 iscsi_trace(TRACE_NET_DEBUG,
738 "connection %d selected\n",
739 sockv[i]);
740 *sock = sockv[i];
741 return i;
746 #endif
749 int
750 iscsi_sock_accept(int sock, int *conn)
752 struct sockaddr_in peer;
753 socklen_t peerlen;
755 peerlen = sizeof(peer);
756 (void) memset(&peer, 0, sizeof(peer));
757 *conn = accept(sock, (struct sockaddr *)(void *)&peer, &peerlen);
758 if (*conn < 0) {
759 iscsi_trace(TRACE_NET_DEBUG,
760 "accept() failed: rc %d errno %d\n", *conn, errno);
761 return 0;
764 return 1;
767 int
768 iscsi_sock_getsockname(int sock, struct sockaddr * name, unsigned *namelen)
770 if (getsockname(sock, name, namelen) != 0) {
771 iscsi_err(__FILE__, __LINE__,
772 "getsockame() failed (errno %d)\n", errno);
773 return 0;
775 return 1;
778 int
779 iscsi_sock_getpeername(int sock, struct sockaddr * name, unsigned *namelen)
781 if (getpeername(sock, name, namelen) != 0) {
782 iscsi_err(__FILE__, __LINE__,
783 "getpeername() failed (errno %d)\n", errno);
784 return 0;
786 return 1;
789 int
790 iscsi_sock_shutdown(int sock, int how)
792 int rc;
794 if ((rc = shutdown(sock, how)) != 0) {
795 iscsi_trace(TRACE_NET_DEBUG,
796 "shutdown() failed: rc %d, errno %d\n", rc, errno);
798 return 0;
801 int
802 iscsi_sock_close(int sock)
804 int rc;
806 if ((rc = close(sock)) != 0) {
807 iscsi_err(__FILE__, __LINE__,
808 "close() failed: rc %d errno %d\n", rc, errno);
809 return -1;
811 return 0;
814 int
815 iscsi_sock_connect(int sock, char *hostname, int port)
817 struct addrinfo hints;
818 struct addrinfo *res;
819 char portstr[32];
820 int rc = 0;
821 int i;
823 (void) memset(&hints, 0, sizeof(hints));
824 hints.ai_family = AF_INET;
825 hints.ai_socktype = SOCK_STREAM;
826 (void) snprintf(portstr, sizeof(portstr), "%d", port);
828 for (i = 0; i < ISCSI_SOCK_CONNECT_TIMEOUT; i++) {
829 /* Attempt connection */
830 #ifdef AI_NUMERICSERV
831 hints.ai_flags = AI_NUMERICSERV;
832 #endif
833 if ((rc = getaddrinfo(hostname, portstr, &hints, &res)) != 0) {
834 hints.ai_flags = 0;
835 if ((rc = getaddrinfo(hostname, "iscsi-target", &hints,
836 &res)) != 0 ||
837 (rc = getaddrinfo(hostname, "iscsi", &hints,
838 &res)) != 0) {
839 iscsi_err(__FILE__, __LINE__,
840 "getaddrinfo: %s", gai_strerror(rc));
841 return 0;
845 #if ISCSI_SOCK_CONNECT_NONBLOCK == 1
846 if (fcntl(sock, F_SETFL, O_NONBLOCK) != 0) {
847 iscsi_err(__FILE__, __LINE__,
848 "fcntl O_NONBLOCK failed");
849 freeaddrinfo(res);
850 return -1;
852 #endif
853 rc = connect(sock, res->ai_addr, res->ai_addrlen);
854 #if ISCSI_SOCK_CONNECT_NONBLOCK == 1
855 if (fcntl(sock, F_SETFL, O_SYNC) != 0) {
856 iscsi_err(__FILE__, __LINE__, "fcntl O_SYNC failed\n");
857 freeaddrinfo(res);
858 return -1;
860 #endif
862 /* Check errno */
863 if (errno == EISCONN) {
864 rc = 0;
865 break;
867 if (errno == EAGAIN ||
868 errno == EINPROGRESS ||
869 errno == EALREADY) {
870 if (i != ISCSI_SOCK_CONNECT_TIMEOUT - 1) {
871 printf("***SLEEPING***\n");
872 sleep(1);
874 } else {
875 break;
878 freeaddrinfo(res);
879 if (rc < 0) {
880 iscsi_err(__FILE__, __LINE__,
881 "connect() to %s:%d failed (errno %d)\n", hostname,
882 port, errno);
884 return rc;
888 * NOTE: iscsi_sock_msg() alters *sg when socket sends and recvs
889 * return having only transfered a portion of the iovec. When this
890 * happens, the iovec is modified and resent with the appropriate
891 * offsets.
894 int
895 iscsi_sock_msg(int sock, int xmit, unsigned len, void *data, int iovc)
897 struct iovec singleton;
898 struct iovec *iov;
899 struct iovec *iov_padding = NULL;
900 unsigned n = 0;
901 uint32_t remainder;
902 uint32_t padding_len = 0;
903 uint8_t padding[ISCSI_SOCK_MSG_BYTE_ALIGN];
904 size_t total_len = 0;
905 int rc;
906 int i;
908 iscsi_trace(TRACE_NET_DEBUG, "%s %d bytes on sock\n",
909 xmit ? "sending" : "receiving", len);
910 if (iovc == 0) {
911 iscsi_trace(TRACE_NET_DEBUG,
912 "building singleton iovec (data %p, len %u)\n",
913 data, len);
914 singleton.iov_base = data;
915 singleton.iov_len = len;
916 iov = &singleton;
917 iovc = 1;
918 } else {
919 iov = (struct iovec *) data;
922 /* Add padding */
924 if ((remainder = len % ISCSI_SOCK_MSG_BYTE_ALIGN) != 0) {
925 iov_padding = iscsi_malloc_atomic((iovc + 1) *
926 sizeof(struct iovec));
927 if (iov_padding == NULL) {
928 iscsi_err(__FILE__, __LINE__,
929 "iscsi_malloc_atomic() failed\n");
930 return -1;
932 memcpy(iov_padding, iov, iovc * sizeof(struct iovec));
933 iov_padding[iovc].iov_base = padding;
934 padding_len = ISCSI_SOCK_MSG_BYTE_ALIGN - remainder;
935 iov_padding[iovc].iov_len = padding_len;
936 iov = iov_padding;
937 iovc++;
938 memset(padding, 0, padding_len);
939 len += padding_len;
940 iscsi_trace(TRACE_NET_DEBUG,
941 "Added iovec for padding (len %u)\n", padding_len);
945 * We make copy of iovec if we're in debugging mode, as we'll
946 * print out the iovec and the buffer contents at the end of
947 * this subroutine and
949 do {
950 /* Check iovec */
952 total_len = 0;
953 iscsi_trace(TRACE_NET_DEBUG, "%s %d buffers\n",
954 xmit ? "gathering from" : "scattering into", iovc);
955 for (i = 0; i < iovc; i++) {
956 iscsi_trace(TRACE_NET_IOV,
957 "iov[%d].iov_base = %p, len %u\n",
958 i, iov[i].iov_base, (unsigned)iov[i].iov_len);
959 total_len += iov[i].iov_len;
961 if (total_len != len - n) {
962 iscsi_err(__FILE__, __LINE__,
963 "iovcs sum to %u != total len of %u\n",
964 total_len, len - n);
965 iscsi_err(__FILE__, __LINE__, "iov = %p\n", iov);
966 for (i = 0; i < iovc; i++) {
967 iscsi_err(__FILE__, __LINE__,
968 "iov[%d].iov_base = %p, len %u\n",
969 i, iov[i].iov_base,
970 (unsigned)iov[i].iov_len);
972 return -1;
974 if ((rc = (xmit) ? writev(sock, iov, iovc) :
975 readv(sock, iov, iovc)) == 0) {
976 iscsi_trace(TRACE_NET_DEBUG,
977 "%s() failed: rc %d errno %d\n",
978 (xmit) ? "writev" : "readv", rc, errno);
979 break;
980 } else if (rc < 0) {
981 /* Temp FIXME */
982 iscsi_err(__FILE__, __LINE__,
983 "%s() failed: rc %d errno %d\n",
984 (xmit) ? "writev" : "readv", rc, errno);
985 break;
987 n += rc;
988 if (n < len) {
989 iscsi_trace(TRACE_NET_DEBUG,
990 "Got partial %s: %d bytes of %u\n",
991 (xmit) ? "send" : "recv", rc, len - n + rc);
992 total_len = 0;
993 for (i = 0; i < iovc; i++) {
994 total_len += iov[i].iov_len;
996 iscsi_trace(TRACE_NET_IOV,
997 "before modify_iov: %s %d buffers, "
998 "total_len = %u, n = %u, rc = %u\n",
999 xmit ? "gathering from" : "scattering into",
1000 iovc, total_len, n, rc);
1001 if (modify_iov(&iov, &iovc, (unsigned) rc, len - n)
1002 != 0) {
1003 iscsi_err(__FILE__, __LINE__,
1004 "modify_iov() failed\n");
1005 break;
1007 total_len = 0;
1008 for (i = 0; i < iovc; i++) {
1009 total_len += iov[i].iov_len;
1011 iscsi_trace(TRACE_NET_IOV,
1012 "after modify_iov: %s %d buffers, "
1013 "total_len = %u, n = %u, rc = %u\n\n",
1014 xmit ? "gathering from" : "scattering into",
1015 iovc, total_len, n, rc);
1017 } while (n < len);
1019 if (remainder) {
1020 iscsi_free_atomic(iov_padding);
1022 iscsi_trace(TRACE_NET_DEBUG,
1023 "successfully %s %u bytes on sock (%u bytes padding)\n",
1024 xmit ? "sent" : "received", n, padding_len);
1025 return n - padding_len;
1029 * Temporary Hack:
1031 * TCP's Nagle algorithm and delayed-ack lead to poor performance when we send
1032 * two small messages back to back (i.e., header+data). The TCP_NODELAY option
1033 * is supposed to turn off Nagle, but it doesn't seem to work on Linux 2.4.
1034 * Because of this, if our data payload is small, we'll combine the header and
1035 * data, else send as two separate messages.
1038 int
1039 iscsi_sock_send_header_and_data(int sock,
1040 void *header, unsigned header_len,
1041 const void *data, unsigned data_len, int iovc)
1043 struct iovec iov[ISCSI_MAX_IOVECS];
1045 if (data_len && data_len <= ISCSI_SOCK_HACK_CROSSOVER) {
1046 /* combine header and data into one iovec */
1047 if (iovc >= ISCSI_MAX_IOVECS) {
1048 iscsi_err(__FILE__, __LINE__,
1049 "iscsi_sock_msg() failed\n");
1050 return -1;
1052 if (iovc == 0) {
1053 iov[0].iov_base = header;
1054 iov[0].iov_len = header_len;
1055 iov[1].iov_base = __UNCONST((const char *)data);
1056 iov[1].iov_len = data_len;
1057 iovc = 2;
1058 } else {
1059 iov[0].iov_base = header;
1060 iov[0].iov_len = header_len;
1061 (void) memcpy(&iov[1], data, sizeof(struct iovec) *
1062 iovc);
1063 iovc += 1;
1065 if ((unsigned)iscsi_sock_msg(sock, Transmit,
1066 header_len + data_len, iov, iovc) !=
1067 header_len + data_len) {
1068 iscsi_err(__FILE__, __LINE__,
1069 "iscsi_sock_msg() failed\n");
1070 return -1;
1072 } else {
1073 if ((unsigned)iscsi_sock_msg(sock, Transmit, header_len,
1074 header, 0) != header_len) {
1075 iscsi_err(__FILE__, __LINE__,
1076 "iscsi_sock_msg() failed\n");
1077 return -1;
1079 if (data_len != 0 &&
1080 (unsigned)iscsi_sock_msg(sock, Transmit, data_len,
1081 __UNCONST((const char *) data), iovc) != data_len) {
1082 iscsi_err(__FILE__, __LINE__,
1083 "iscsi_sock_msg() failed\n");
1084 return -1;
1087 return header_len + data_len;
1091 /* spin lock functions */
1092 int
1093 iscsi_spin_init(iscsi_spin_t * lock)
1095 pthread_mutexattr_t mattr;
1097 pthread_mutexattr_init(&mattr);
1098 #ifdef PTHREAD_MUTEX_ADAPTIVE_NP
1099 pthread_mutexattr_settype(&mattr, PTHREAD_MUTEX_ADAPTIVE_NP);
1100 #endif
1101 if (pthread_mutex_init(lock, &mattr) != 0)
1102 return -1;
1103 return 0;
1106 int
1107 iscsi_spin_lock(iscsi_spin_t * lock)
1109 return pthread_mutex_lock(lock);
1112 int
1113 iscsi_spin_unlock(iscsi_spin_t * lock)
1115 return pthread_mutex_unlock(lock);
1118 /* ARGSUSED1 */
1119 int
1120 iscsi_spin_lock_irqsave(iscsi_spin_t * lock, uint32_t *flags)
1122 return pthread_mutex_lock(lock);
1125 /* ARGSUSED1 */
1126 int
1127 iscsi_spin_unlock_irqrestore(iscsi_spin_t * lock, uint32_t *flags)
1129 return pthread_mutex_unlock(lock);
1132 int
1133 iscsi_spin_destroy(iscsi_spin_t * lock)
1135 return pthread_mutex_destroy(lock);
1140 * Mutex Functions, kernel module doesn't require mutex for locking.
1141 * For thread sync, 'down' & 'up' have been wrapped into condition
1142 * varibles, which is kernel semaphores in kernel module.
1145 int
1146 iscsi_mutex_init(iscsi_mutex_t * m)
1148 return (pthread_mutex_init(m, NULL) != 0) ? -1 : 0;
1151 int
1152 iscsi_mutex_lock(iscsi_mutex_t * m)
1154 return pthread_mutex_lock(m);
1157 int
1158 iscsi_mutex_unlock(iscsi_mutex_t * m)
1160 return pthread_mutex_unlock(m);
1163 int
1164 iscsi_mutex_destroy(iscsi_mutex_t * m)
1166 return pthread_mutex_destroy(m);
1170 * Condition Functions
1173 int
1174 iscsi_cond_init(iscsi_cond_t * c)
1176 return pthread_cond_init(c, NULL);
1179 int
1180 iscsi_cond_wait(iscsi_cond_t * c, iscsi_mutex_t * m)
1182 return pthread_cond_wait(c, m);
1185 int
1186 iscsi_cond_signal(iscsi_cond_t * c)
1188 return pthread_cond_signal(c);
1191 int
1192 iscsi_cond_destroy(iscsi_cond_t * c)
1194 return pthread_cond_destroy(c);
1198 * Misc. Functions
1201 uint32_t
1202 iscsi_atoi(char *value)
1204 if (value == NULL) {
1205 iscsi_err(__FILE__, __LINE__,
1206 "iscsi_atoi() called with NULL value\n");
1207 return 0;
1209 return atoi(value);
1212 static const char HexString[] = "0123456789abcdef";
1214 /* get the hex value (subscript) of the character */
1215 static int
1216 HexStringIndex(const char *s, int c)
1218 const char *cp;
1220 return (c == '0') ? 0 :
1221 ((cp = strchr(s, tolower(c))) == NULL) ? -1 : (int)(cp - s);
1224 int
1225 HexDataToText(uint8_t *data, uint32_t dataLength,
1226 char *text, uint32_t textLength)
1228 uint32_t n;
1230 if (!text || textLength == 0) {
1231 return -1;
1233 if (!data || dataLength == 0) {
1234 *text = 0x0;
1235 return -1;
1237 if (textLength < 3) {
1238 *text = 0x0;
1239 return -1;
1241 *text++ = '0';
1242 *text++ = 'x';
1244 textLength -= 2;
1246 while (dataLength > 0) {
1248 if (textLength < 3) {
1249 *text = 0x0;
1250 return -1;
1252 n = *data++;
1253 dataLength--;
1255 *text++ = HexString[(n >> 4) & 0xf];
1256 *text++ = HexString[n & 0xf];
1258 textLength -= 2;
1261 *text = 0x0;
1263 return 0;
1267 int
1268 HexTextToData(const char *text, uint32_t textLength,
1269 uint8_t *data, uint32_t dataLength)
1271 int i;
1272 uint32_t n1;
1273 uint32_t n2;
1274 uint32_t len = 0;
1276 if ((text[0] == '0') && (text[1] != 'x' || text[1] != 'X')) {
1277 /* skip prefix */
1278 text += 2;
1279 textLength -= 2;
1281 if ((textLength % 2) == 1) {
1283 i = HexStringIndex(HexString, *text++);
1284 if (i < 0)
1285 return -1; /* error, bad character */
1287 n2 = i;
1289 if (dataLength < 1) {
1290 return -1; /* error, too much data */
1292 *data++ = n2;
1293 len++;
1295 while (*text != 0x0) {
1297 if ((i = HexStringIndex(HexString, *text++)) < 0) {
1298 /* error, bad character */
1299 return -1;
1302 n1 = i;
1304 if (*text == 0x0) {
1305 /* error, odd string length */
1306 return -1;
1309 if ((i = HexStringIndex(HexString, *text++)) < 0) {
1310 /* error, bad character */
1311 return -1;
1314 n2 = i;
1316 if (len >= dataLength) {
1317 /* error, too much data */
1318 return len;
1320 *data++ = (n1 << 4) | n2;
1321 len++;
1324 return (len == 0) ? -1 : 0;
1327 void
1328 GenRandomData(uint8_t *data, uint32_t length)
1330 uint32_t n;
1331 size_t i;
1333 for (i = 0 ; i < length ; i += sizeof(n)) {
1334 n = random();
1335 (void) memcpy(&data[i], &n, MIN(length - i, sizeof(n)));