1 /* $NetBSD: isns_pdu.c,v 1.1.1.1 2011/01/16 01:22:50 agc Exp $ */ 2 3 /*- 4 * Copyright (c) 2004,2009 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Wasabi Systems, Inc. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 /* 33 * isns_pdu.c 34 */ 35 36 37 #include <sys/cdefs.h> 38 __RCSID("$NetBSD: isns_pdu.c,v 1.1.1.1 2011/01/16 01:22:50 agc Exp $"); 39 40 41 #include <sys/types.h> 42 #include <sys/param.h> 43 44 #include <assert.h> 45 #include <errno.h> 46 #include <string.h> 47 48 #include "isns.h" 49 #include "isns_config.h" 50 51 52 /* 53 * Local function prototypes. 54 */ 55 static struct isns_buffer_list_s *isns_lookup_buffer_list(int); 56 57 static struct isns_pdu_s *isns_init_pdu(struct isns_buffer_s *, 58 struct isns_config_s *, uint16_t, uint16_t, uint16_t); 59 static int isns_add_pdu_payload_data(struct isns_trans_s *, const void *, int); 60 static void isns_get_tlv_info_advance(struct isns_get_tlv_info_s *); 61 static int isns_get_tlv_uint32(struct isns_get_tlv_info_s *, uint32_t *); 62 static int isns_get_tlv_data(struct isns_get_tlv_info_s *, int, void **); 63 64 static void isns_add_pdu_list(struct isns_pdu_s **, struct isns_pdu_s *); 65 static struct isns_buffer_s *isns_get_pdu_head_buffer(struct isns_pdu_s *); 66 #if 0 67 static struct isns_buffer_s *isns_get_pdu_tail_buffer(struct isns_pdu_s *); 68 #endif 69 static struct isns_buffer_s *isns_get_pdu_active_buffer(struct isns_pdu_s *); 70 71 static uint32_t isns_get_next_trans_id(void); 72 73 /* 74 * Buffer pool structures and global (static) var. 75 */ 76 struct isns_buffer_list_s { 77 int buf_size; 78 int alloc_count; 79 struct isns_buffer_s *head; 80 struct isns_buffer_list_s *next; 81 }; 82 83 struct isns_buffer_pool_s { 84 int active; 85 struct isns_buffer_list_s *list_p; 86 pthread_mutex_t mutex; 87 }; 88 89 static struct isns_buffer_pool_s G_buffer_pool; 90 91 92 /* 93 * isns_init_buffer_pool - initialize buffer pool for use 94 */ 95 void 96 isns_init_buffer_pool() 97 { 98 pthread_mutexattr_t mutexattr; 99 100 DBG("isns_init_buffer_pool: entered\n"); 101 102 assert(!G_buffer_pool.active); 103 104 pthread_mutexattr_init(&mutexattr); 105 pthread_mutexattr_settype(&mutexattr, ISNS_MUTEX_TYPE_NORMAL); 106 pthread_mutex_init(&G_buffer_pool.mutex, &mutexattr); 107 108 G_buffer_pool.active = 1; 109 } 110 111 112 /* 113 * isns_lookup_buffer_list - locates the pool buffer list for the buf_size 114 * specified. 115 * 116 * Returns: pointer to list in pool containing buf_size buffers 117 * NULL if no list for size indicated exists 118 */ 119 static struct isns_buffer_list_s * 120 isns_lookup_buffer_list(int buf_size) 121 { 122 struct isns_buffer_list_s *list_p; 123 124 /* 125 * WARNING: G_buffer_pool.mutex MUST already be locked. 126 */ 127 128 list_p = G_buffer_pool.list_p; 129 while (list_p != NULL) { 130 if (list_p->buf_size == buf_size) 131 break; 132 list_p = list_p->next; 133 } 134 135 return list_p; 136 } 137 138 139 /* 140 * isns_add_buffer_pool - allocates buffers of in pool 141 * 142 * If a list containing buf_size buffers already exists in pool, additional 143 * buffers are added (allocated) to the list. 144 */ 145 int 146 isns_add_buffer_pool(int buf_size, int count) 147 { 148 struct isns_buffer_list_s *list_p, *p, *p_next; 149 struct isns_buffer_s *buf_p; 150 int n; 151 152 DBG("isns_add_buffer_pool: buf_size=%d, count=%d\n", buf_size, count); 153 154 assert(G_buffer_pool.active); 155 156 /* Make our buffer lengths always a multiple of 4. */ 157 buf_size = (buf_size + 0x03) & ~0x03; 158 159 /* 160 * Lookup existing list for size specified. If no exists, allocate 161 * a new list and initialize. 162 */ 163 pthread_mutex_lock(&G_buffer_pool.mutex); 164 list_p = isns_lookup_buffer_list(buf_size); 165 if (list_p == NULL) { 166 pthread_mutex_unlock(&G_buffer_pool.mutex); 167 list_p = (struct isns_buffer_list_s *) 168 isns_malloc(sizeof(struct isns_buffer_list_s)); 169 if (list_p == NULL) { 170 DBG("isns_add_buffer_pool: error on isns_malloc()\n"); 171 return ENOMEM; 172 } 173 list_p->buf_size = buf_size; 174 list_p->alloc_count = 0; 175 list_p->head = NULL; 176 } 177 178 /* If this is a new list, insert into pool in buf_size order. */ 179 if (list_p->alloc_count == 0) { 180 pthread_mutex_lock(&G_buffer_pool.mutex); 181 if (G_buffer_pool.list_p == NULL) { 182 G_buffer_pool.list_p = list_p; 183 list_p->next = NULL; 184 } else if (G_buffer_pool.list_p->buf_size > buf_size) { 185 list_p->next = G_buffer_pool.list_p; 186 G_buffer_pool.list_p = list_p; 187 } else { 188 p = G_buffer_pool.list_p; 189 while (p->next != NULL) { 190 p_next = p->next; 191 if (p_next->buf_size > buf_size) { 192 p->next = list_p; 193 list_p->next = p_next; 194 break; 195 } 196 p = p->next; 197 } 198 if (p->next == NULL) { 199 p->next = list_p; 200 list_p->next = NULL; 201 } 202 } 203 } 204 205 /* Allocate (possibly additional) buffers for list. */ 206 for (n = 0; n < count; n++) { 207 buf_p = (struct isns_buffer_s *) 208 isns_malloc(buf_size + sizeof(struct isns_buffer_s)); 209 if (buf_p == NULL) 210 break; 211 buf_p->next = list_p->head; 212 list_p->head = buf_p; 213 } 214 list_p->alloc_count += n; 215 pthread_mutex_unlock(&G_buffer_pool.mutex); 216 217 DBG("isns_init_buffer_pool: %d %d-byte buffers allocated\n", 218 n, buf_size); 219 220 return (n > 0 ? 0 : ENOMEM); 221 } 222 223 224 /* 225 * isns_destroy_buffer_pool - destroys previously allocated buffer pool 226 */ 227 void 228 isns_destroy_buffer_pool(void) 229 { 230 struct isns_buffer_list_s *list_p; 231 struct isns_buffer_s *buf_p; 232 #ifdef ISNS_DEBUG 233 char dbg_buffer[1024] = { 0 }; 234 #endif 235 236 DBG("isns_destroy_buffer_pool: entered\n"); 237 238 assert(G_buffer_pool.active); 239 240 pthread_mutex_lock(&G_buffer_pool.mutex); 241 while (G_buffer_pool.list_p != NULL) { 242 list_p = G_buffer_pool.list_p; 243 while (list_p->head != NULL) { 244 buf_p = list_p->head; 245 list_p->head = buf_p->next; 246 list_p->alloc_count--; 247 isns_free(buf_p); 248 } 249 #ifdef ISNS_DEBUG 250 if (list_p->alloc_count > 0) { 251 snprintf(&dbg_buffer[(int) strlen(dbg_buffer)], 252 (sizeof(dbg_buffer) - strlen(dbg_buffer)), 253 "isns_destroy_buffer_pool: " 254 "%d %d-byte buffer(s) not freed\n", 255 list_p->alloc_count, list_p->buf_size); 256 } 257 #endif 258 G_buffer_pool.list_p = list_p->next; 259 isns_free(list_p); 260 } 261 G_buffer_pool.active = 0; 262 263 pthread_mutex_unlock(&G_buffer_pool.mutex); 264 pthread_mutex_destroy(&G_buffer_pool.mutex); 265 266 DBG(dbg_buffer); 267 } 268 269 270 /* 271 * isns_new_buffer - allocates a new ISNS buffer 272 * 273 * Typically, the buffer is returned from the pool, but if no free buffers 274 * are available in the pool, or a buf size larger than the largest pool buffer 275 * size is requested, a normal malloc is used to allocate the buffer. The 276 * buffer type is recorded so that a subsequent isns_free_buffer will correctly 277 * free the buffer or return it to the pool. 278 */ 279 struct isns_buffer_s * 280 isns_new_buffer(int buf_size) 281 { 282 struct isns_buffer_list_s *list_p; 283 struct isns_buffer_s *buf_p; 284 int buf_type; 285 286 if (buf_size == 0) 287 buf_size = ISNS_BUF_SIZE; 288 buf_p = NULL; 289 290 pthread_mutex_lock(&G_buffer_pool.mutex); 291 list_p = G_buffer_pool.list_p; 292 while (list_p != NULL) { 293 if ((list_p->head != NULL) 294 && (list_p->buf_size >= buf_size)) { 295 buf_p = list_p->head; 296 list_p->head = buf_p->next; 297 buf_size = list_p->buf_size; 298 buf_type = ISNS_BUF_POOL; 299 break; 300 } 301 list_p = list_p->next; 302 } 303 pthread_mutex_unlock(&G_buffer_pool.mutex); 304 305 if (buf_p == NULL) { 306 buf_p = (struct isns_buffer_s *)isns_malloc( 307 buf_size + sizeof(struct isns_buffer_s)); 308 buf_type = ISNS_BUF_MALLOC; 309 } 310 311 if (buf_p != NULL) 312 ISNS_INIT_BUFFER(buf_p, buf_size, buf_type); 313 314 DBG("isns_new_buffer: %p (buf_size=%d, type=%d)\n", buf_p, buf_size, 315 buf_type); 316 317 return buf_p; 318 } 319 320 321 /* 322 * isns_free_buffer - free a ISNS buffer 323 */ 324 void 325 isns_free_buffer(struct isns_buffer_s *buf_p) 326 { 327 struct isns_buffer_list_s *list_p; 328 329 DBG("isns_free_buffer: %p (type=%d, alloc_len=%d)\n", 330 buf_p, (buf_p == NULL ? 0 : buf_p->buf_type), 331 (buf_p == NULL ? 0 : buf_p->alloc_len)); 332 333 if (buf_p != NULL) { 334 switch (buf_p->buf_type) { 335 case ISNS_BUF_POOL: 336 /* Return buffer to proper pool list. */ 337 pthread_mutex_lock(&G_buffer_pool.mutex); 338 list_p = isns_lookup_buffer_list((int)buf_p->alloc_len); 339 if (list_p != NULL) { 340 buf_p->next = list_p->head; 341 list_p->head = buf_p; 342 } 343 pthread_mutex_unlock(&G_buffer_pool.mutex); 344 break; 345 case ISNS_BUF_MALLOC: 346 /* Malloc allocated buf, so free normally. */ 347 isns_free(buf_p); 348 break; 349 case ISNS_BUF_STATIC: 350 /* Static buf with no allocation, so do nothing here. */ 351 break; 352 } 353 } 354 } 355 356 357 /* 358 * isns_new_trans - create a new ISNS transaction 359 */ 360 ISNS_TRANS 361 isns_new_trans(ISNS_HANDLE isns_handle, uint16_t func_id, uint16_t pdu_flags) 362 { 363 struct isns_trans_s *trans_p; 364 struct isns_pdu_s *pdu_p; 365 struct isns_buffer_s *buf_p; 366 367 if (isns_handle == ISNS_INVALID_HANDLE) { 368 DBG("isns_new_trans: error - handle=%p\n", isns_handle); 369 return ISNS_INVALID_TRANS; 370 } 371 372 buf_p = isns_new_buffer((int)sizeof(struct isns_trans_s)); 373 if (buf_p == NULL) { 374 DBG("isns_new_trans: error on isns_new_buffer()\n"); 375 return ISNS_INVALID_TRANS; 376 } 377 378 trans_p = (struct isns_trans_s *)isns_buffer_data(buf_p, 0); 379 trans_p->id = isns_get_next_trans_id(); 380 trans_p->func_id = func_id; 381 trans_p->flags = 0; 382 trans_p->cfg_p = (struct isns_config_s *)isns_handle; 383 trans_p->pdu_req_list = NULL; 384 trans_p->pdu_rsp_list = NULL; 385 trans_p->disconnect_cnt = 0; 386 387 trans_p->get_tlv_info.pdu_p = NULL; 388 trans_p->get_tlv_info.buf_p = NULL; 389 trans_p->get_tlv_info.extra_buf_list = NULL; 390 trans_p->get_tlv_info.buf_ofs = 0; 391 392 buf_p->cur_len = sizeof(struct isns_trans_s); 393 394 /* 395 * Mask off all but the AUTH and possibly REPLACE_REG pdu flags. Then, 396 * set the appropriate server/client sender flag. The first/last PDU 397 * flags will be set when the PDU is sent. 398 */ 399 if (func_id == isnsp_DevAttrReg) 400 pdu_flags &= (ISNS_FLAG_AUTH | ISNS_FLAG_REPLACE_REG); 401 else 402 pdu_flags &= ISNS_FLAG_AUTH; 403 404 if (trans_p->cfg_p->is_server) 405 pdu_flags |= ISNS_FLAG_SND_SERVER; 406 else 407 pdu_flags |= ISNS_FLAG_SND_CLIENT; 408 409 pdu_p = isns_new_pdu(trans_p->cfg_p, trans_p->id, func_id, pdu_flags); 410 if (pdu_p == NULL) { 411 DBG("isns_new_trans: error on isns_new_pdu()\n"); 412 isns_free_buffer(buf_p); 413 return ISNS_INVALID_TRANS; 414 } 415 416 isns_add_pdu_request((ISNS_TRANS)trans_p, pdu_p); 417 418 DBG("isns_new_trans: %p\n", trans_p); 419 420 return (ISNS_TRANS)trans_p; 421 } 422 423 424 /* 425 * isns_free_trans - free ISNS transaction created with isns_new_trans 426 */ 427 void 428 isns_free_trans(ISNS_TRANS trans) 429 { 430 struct isns_trans_s *trans_p; 431 struct isns_pdu_s *pdu_p; 432 struct isns_buffer_s *buf_p, *free_buf_p; 433 uint32_t trans_flags; 434 435 DBG("isns_free_trans: %p\n", trans); 436 437 if (trans != ISNS_INVALID_TRANS) { 438 trans_p = (struct isns_trans_s *)trans; 439 440 trans_flags = isns_set_trans_flags(trans_p, 441 ISNS_TRANSF_FREE_WHEN_COMPLETE); 442 443 if ((trans_flags & ISNS_TRANSF_COMPLETE) == 0) { 444 DBG("isns_free_trans: deferred - trans not complete\n"); 445 return; 446 } 447 448 DBG("isns_free_trans: pdu_req_list=%p\n", 449 trans_p->pdu_req_list); 450 while ((pdu_p = trans_p->pdu_req_list) != NULL) { 451 trans_p->pdu_req_list = pdu_p->next; 452 isns_free_pdu(pdu_p); 453 } 454 DBG("isns_free_trans: pdu_rsp_list=%p\n", 455 trans_p->pdu_rsp_list); 456 while ((pdu_p = trans_p->pdu_rsp_list) != NULL) { 457 trans_p->pdu_rsp_list = pdu_p->next; 458 isns_free_pdu(pdu_p); 459 } 460 DBG("isns_free_trans: extra_buf_list=%p\n", 461 trans_p->get_tlv_info.extra_buf_list); 462 buf_p = trans_p->get_tlv_info.extra_buf_list; 463 while (buf_p != NULL) { 464 free_buf_p = buf_p; 465 buf_p = buf_p->next; 466 isns_free_buffer(free_buf_p); 467 } 468 469 DBG("isns_free_trans: freeing base trans buffer\n"); 470 buf_p = ((struct isns_buffer_s *)(void *)(trans_p))-1; 471 isns_free_buffer(buf_p); 472 } 473 } 474 475 476 /* 477 * isns_send_trans - send ISNS transaction PDU(s) and optionally wait 478 * 479 * If a successful wait occurs (i.e., the transaction completes without 480 * a timeout), then the response PDU status is place in *status_p. For 481 * all other cases, the data returned in *status_p is undefined. 482 * 483 */ 484 int 485 isns_send_trans(ISNS_TRANS trans, const struct timespec *timeout_p, 486 uint32_t *status_p) 487 { 488 struct isns_trans_s *trans_p; 489 struct isns_pdu_s *pdu_p; 490 int rval; 491 492 trans_p = (struct isns_trans_s *)trans; 493 494 DBG("isns_send_trans: trans_p=%p, timeout_p=%p\n", trans_p, timeout_p); 495 496 if (status_p != NULL) 497 *status_p = 0; 498 499 if (!isns_is_socket_init_done(trans_p->cfg_p)) { 500 DBG("isns_send_trans: socket not initialized\n"); 501 isns_complete_trans(trans_p); 502 return EINVAL; 503 } 504 505 if ((pdu_p = isns_get_pdu_request(trans)) == NULL) { 506 DBG("isns_send_trans: no request PDU\n"); 507 return EINVAL; 508 } 509 510 /* Set the FIRST_PDU flag in the first PDU. */ 511 pdu_p->hdr.flags |= ISNS_FLAG_FIRST_PDU; 512 513 /* Set our PDU sequence numbers for the PDU chain. */ 514 while (pdu_p->next != NULL) { 515 pdu_p->next->hdr.seq_id = pdu_p->hdr.seq_id + 1; 516 pdu_p = pdu_p->next; 517 } 518 519 /* Set the LAST_PDU flag in the last PDU. */ 520 pdu_p->hdr.flags |= ISNS_FLAG_LAST_PDU; 521 522 rval = isns_send_pdu(trans, isns_get_pdu_request(trans), timeout_p); 523 if ((rval == 0) && (status_p != NULL)) 524 isns_get_pdu_response_status(trans, status_p); 525 526 return rval; 527 } 528 529 530 531 void 532 isns_complete_trans(struct isns_trans_s *trans_p) 533 { 534 uint32_t flags; 535 536 DBG("isns_complete_trans: trans_p=%p\n", trans_p); 537 538 flags = isns_set_trans_flags(trans_p, ISNS_TRANSF_COMPLETE); 539 540 if ((flags & ISNS_TRANSF_FREE_WHEN_COMPLETE) != 0) 541 isns_free_trans(trans_p); 542 } 543 544 545 int 546 isns_abort_trans(struct isns_config_s *cfg_p, uint16_t trans_id) 547 { 548 struct isns_task_s *task_p; 549 550 /* First, look at current task. */ 551 if (((task_p = cfg_p->curtask_p) != NULL) 552 && (task_p->task_type == ISNS_TASK_SEND_PDU) 553 && (task_p->var.send_pdu.trans_p->id == trans_id)) { 554 isns_complete_trans(task_p->var.send_pdu.trans_p); 555 isns_end_task(task_p); 556 return 0; 557 } 558 559 /* If not current task, look in task queue. */ 560 task_p = isns_taskq_remove_trans(cfg_p, trans_id); 561 if (task_p) { 562 isns_complete_trans(task_p->var.send_pdu.trans_p); 563 isns_end_task(task_p); 564 return 0; 565 } 566 567 return EINVAL; 568 } 569 570 /* 571 * isns_add_string - add a TLV which is a C string 572 * 573 * Wrapper around isns_add_tlv() 574 */ 575 int 576 isns_add_string(ISNS_TRANS trans, uint32_t tag, const char *s) 577 { 578 /* Add string, including required NULL. */ 579 return isns_add_tlv(trans, tag, (int)strlen(s)+1, s); 580 } 581 582 583 /* 584 * isns_add_tlv - adds a TLV to an existing transaction 585 */ 586 int 587 isns_add_tlv(ISNS_TRANS trans, uint32_t tag, int data_len, const void *data_p) 588 { 589 struct isns_trans_s *trans_p; 590 uint8_t tlv_buf[ISNS_TLV_HDR_SIZE]; 591 int rval; 592 593 DBG("isns_add_tlv: trans=%p, tag=%d, data_len=%d, data_p=%p\n", 594 trans, tag, data_len, data_p); 595 596 if (trans == ISNS_INVALID_TRANS) { 597 DBG("isns_add_tlv: error - trans=%p\n", trans); 598 return EINVAL; 599 } 600 601 if ((data_len > 0) && (data_p == NULL)) { 602 DBG("isns_add_tlv: error data_len=%d, data_p=%p\n", 603 data_len, data_p); 604 return EINVAL; 605 } 606 607 /* Set tag, length in header buffer and add to PDU payload data. */ 608 trans_p = (struct isns_trans_s *)trans; 609 ISNS_TLV_SET_TAG(tlv_buf, tag); 610 ISNS_TLV_SET_LEN(tlv_buf, ISNS_PAD4_LEN(data_len)); 611 rval = isns_add_pdu_payload_data(trans_p, tlv_buf, ISNS_TLV_HDR_SIZE); 612 613 /* If header added okay, add value portion to PDU payload data. */ 614 if ((rval == 0) && (data_len > 0)) 615 rval = isns_add_pdu_payload_data(trans_p, data_p, data_len); 616 617 return rval; 618 } 619 620 621 /* 622 * isns_get_tlv - get TLV value from response PDU for transaction 623 * 624 * returns: 625 * 0 - success 626 * ENOENT - no (more) TLVs in this transaction 627 * EINVAL - invalid arg 628 * EPERM - operation not permitted - transaction not complete 629 * ENOMEM - could not allocate storage for spanning TLV data 630 */ 631 int 632 isns_get_tlv(ISNS_TRANS trans, int which_tlv, uint32_t *tag_p, int *data_len_p, 633 void **data_pp) 634 { 635 struct isns_trans_s *trans_p; 636 struct isns_get_tlv_info_s *info_p; 637 struct isns_pdu_s *pdu_p; 638 int rval; 639 640 if (trans == ISNS_INVALID_TRANS) { 641 DBG("isns_get_tlv: error - trans=%p\n", trans); 642 return EINVAL; 643 } 644 645 trans_p = (struct isns_trans_s *)trans; 646 if ((isns_get_trans_flags(trans_p) & ISNS_TRANSF_COMPLETE) == 0) { 647 DBG("isns_get_tlv: error - trans not complete\n"); 648 return EPERM; 649 } 650 651 /* Get response PDU for this transaction. */ 652 pdu_p = isns_get_pdu_response(trans_p); 653 if (pdu_p == NULL) { 654 DBG("isns_get_tlv: error - no response PDU in transaction\n"); 655 return EINVAL; 656 } 657 658 if (pdu_p->payload_p->cur_len == 0) { 659 DBG("isns_get_tlv: error - zero length PDU payload\n"); 660 return EINVAL; 661 } 662 663 /* If get_tlv_info unset, treat ISNS_TLV_NEXT as ISNS_TLV_FIRST. */ 664 info_p = &trans_p->get_tlv_info; 665 if ((which_tlv == ISNS_TLV_NEXT) && (info_p->pdu_p == NULL)) 666 which_tlv = ISNS_TLV_FIRST; 667 668 /*!!! make sure PDU uses TLVs here */ 669 670 switch (which_tlv) { 671 case ISNS_TLV_NEXT: 672 /* For next TLV, nothing to do here - use get_tlv_info as-is. */ 673 break; 674 675 case ISNS_TLV_FIRST: 676 /* For first TLV, reset get_tlv_info. */ 677 info_p->pdu_p = pdu_p; 678 info_p->buf_p = isns_get_pdu_head_buffer(pdu_p); 679 info_p->buf_ofs = 4; 680 break; 681 682 default: 683 DBG("isns_get_tlv: invalid arg (which_tlv=%d)\n", which_tlv); 684 return EINVAL; 685 } 686 687 /* 688 * Get the type, length, and data (value) for the TLV. The get calls 689 * below will advance the pointers in get_tlv_info_s *info_p. Note that 690 * if the get of the TAG type fails, ENOENT is returned indicating that 691 * no more TLVs exist for this request PDU. 692 */ 693 if ((rval = isns_get_tlv_uint32(info_p, tag_p)) != 0) { 694 DBG("isns_get_tlv: error on isns_get_tlv_uint32() tag\n"); 695 return ENOENT; 696 } 697 698 if ((rval = isns_get_tlv_uint32(info_p, (uint32_t *)data_len_p)) != 0) { 699 DBG("isns_get_tlv: error on isns_get_tlv_uint32() data len\n"); 700 return rval; 701 } 702 703 rval = isns_get_tlv_data(info_p, *data_len_p, data_pp); 704 if (rval != 0) { 705 DBG("isns_get_tlv: error on isns_get_tlv_data()\n"); 706 return rval; 707 } 708 709 return 0; 710 } 711 712 713 /* 714 * isns_set_trans_flags - sets flag bit(s) in transaction flags member 715 */ 716 uint32_t 717 isns_set_trans_flags(struct isns_trans_s *trans_p, uint32_t flags) 718 { 719 pthread_mutex_lock(&trans_p->cfg_p->trans_mutex); 720 trans_p->flags |= flags; 721 flags = trans_p->flags; 722 pthread_mutex_unlock(&trans_p->cfg_p->trans_mutex); 723 724 return flags; 725 } 726 727 728 /* 729 * isns_add_pdu_request - adds PDU to transaction request PDU list 730 */ 731 void 732 isns_add_pdu_request(struct isns_trans_s *trans_p, struct isns_pdu_s *pdu_p) 733 { 734 DBG("isns_add_pdu_request: trans_p=%p, pdu_p=%p\n", trans_p, pdu_p); 735 736 isns_add_pdu_list(&trans_p->pdu_req_list, pdu_p); 737 } 738 739 740 /* 741 * isns_add_pdu_response - adds PDU to transaction response PDU list 742 */ 743 void 744 isns_add_pdu_response(struct isns_trans_s *trans_p, struct isns_pdu_s *pdu_p) 745 { 746 DBG("isns_add_pdu_response: trans_p=%p, pdu_p=%p\n", trans_p, pdu_p); 747 748 isns_add_pdu_list(&trans_p->pdu_rsp_list, pdu_p); 749 } 750 751 752 /* 753 * isns_get_pdu_request_tail - returns last PDU in request PDU chain 754 */ 755 struct isns_pdu_s * 756 isns_get_pdu_request_tail(struct isns_trans_s *trans_p) 757 { 758 struct isns_pdu_s *pdu_p; 759 760 if ((pdu_p = isns_get_pdu_request(trans_p)) != NULL) { 761 while (pdu_p->next != NULL) 762 pdu_p = pdu_p->next; 763 } 764 765 return pdu_p; 766 } 767 768 769 /* 770 * isns_new_pdu - allocates a new PDU and assigns funtion ID and flags 771 */ 772 struct isns_pdu_s * 773 isns_new_pdu(struct isns_config_s *cfg_p, uint16_t trans_id, uint16_t func_id, 774 uint16_t flags) 775 { 776 struct isns_buffer_s *buf_p; 777 778 /* 779 * Allocate a buffer at least large enough for our isns_pdu_s struct 780 * and the embedded isns_buffer_s struct for the PDU payload data and 4 781 * bytes of actual payload data. 782 */ 783 buf_p = isns_new_buffer(/* CONSTCOND */(int)MAX(ISNS_BUF_SIZE, 784 sizeof(struct isns_pdu_s) + sizeof(struct isns_buffer_s) + 4)); 785 if (buf_p == NULL) { 786 DBG("isns_new_pdu: error on isns_new_buffer()\n"); 787 return NULL; 788 } 789 790 return isns_init_pdu(buf_p, cfg_p, trans_id, func_id, flags); 791 } 792 793 794 /* 795 * isns_free_pdu - frees a PDU and all associated buffers 796 */ 797 void 798 isns_free_pdu(struct isns_pdu_s *pdu_p) 799 { 800 struct isns_buffer_s *buf_p, *free_buf_p; 801 802 DBG("isns_free_pdu: %p\n", pdu_p); 803 804 if (pdu_p != NULL) { 805 /* Free all payload buffers. */ 806 buf_p = pdu_p->payload_p; 807 while (buf_p != NULL) { 808 free_buf_p = buf_p; 809 buf_p = buf_p->next; 810 isns_free_buffer(free_buf_p); 811 } 812 /* 813 * Get a pointer to the ISNS buffer in which this PDU is 814 * contained, and then free it. 815 */ 816 buf_p = ((struct isns_buffer_s *)(void *)(pdu_p))-1 ; 817 isns_free_buffer(buf_p); 818 } 819 } 820 821 822 /* 823 * isns_send_pdu - initiates the send PDU task 824 */ 825 int 826 isns_send_pdu(ISNS_TRANS trans, struct isns_pdu_s *pdu_p, 827 const struct timespec *timeout_p) 828 { 829 struct isns_trans_s *trans_p; 830 struct isns_task_s* task_p; 831 int rval; 832 833 if (trans == ISNS_INVALID_TRANS) { 834 DBG("isns_send_pdu: error - trans=%p\n", trans); 835 return EINVAL; 836 } 837 838 if (pdu_p == NULL) { 839 DBG("isns_send_pdu: error - pdu_p=%p\n", pdu_p); 840 return EINVAL; 841 } 842 843 trans_p = (struct isns_trans_s *)trans; 844 845 /* Build SEND_PDU task, insert on queue and issue command. */ 846 task_p = isns_new_task(pdu_p->cfg_p, ISNS_TASK_SEND_PDU, 847 (timeout_p != NULL)); 848 task_p->var.send_pdu.trans_p = trans_p; 849 task_p->var.send_pdu.pdu_p = pdu_p; 850 851 isns_taskq_insert_tail(pdu_p->cfg_p, task_p); 852 853 isns_issue_cmd(pdu_p->cfg_p, ISNS_CMD_PROCESS_TASKQ); 854 855 if (timeout_p == NULL) 856 rval = 0; 857 else { 858 rval = isns_wait_task(task_p, timeout_p); 859 if (rval == ETIMEDOUT) { 860 DBG("isns_send_pdu: " 861 "timeout on isns_wait_task() trans_id=%d\n", 862 trans_p->id); 863 864 isns_issue_cmd_with_data(task_p->cfg_p, 865 ISNS_CMD_ABORT_TRANS, (void *)&trans_p->id, 866 (int)sizeof(trans_p->id)); 867 } 868 } 869 870 return rval; 871 } 872 873 874 /* 875 * isns_init_pdu - initialize ISNS buffer to be a PDU 876 */ 877 static struct isns_pdu_s * 878 isns_init_pdu(struct isns_buffer_s *buf_p, struct isns_config_s *cfg_p, 879 uint16_t trans_id, uint16_t func_id, uint16_t flags) 880 { 881 struct isns_pdu_s *pdu_p; 882 883 /* The config and buffer pointers must be valid here. */ 884 assert(cfg_p != NULL); 885 assert(buf_p != NULL); 886 887 /* The PDU starts at offset 0 for the ISNS buffer data. */ 888 pdu_p = isns_buffer_data(buf_p, 0); 889 buf_p->cur_len = sizeof(struct isns_pdu_s); 890 891 /* Assign PDU members. */ 892 pdu_p->cfg_p = cfg_p; 893 pdu_p->hdr.isnsp_version = ISNSP_VERSION; 894 pdu_p->hdr.func_id = func_id; 895 pdu_p->hdr.payload_len = 0; 896 pdu_p->hdr.flags = flags; 897 pdu_p->hdr.trans_id = trans_id; 898 pdu_p->hdr.seq_id = 0; 899 pdu_p->byteorder_host = 1; 900 pdu_p->next = NULL; 901 902 /* 903 * The PDU payload buffer starts after the PDU struct portion in the 904 * ISNS buffer passed in to this function. So, assign the payload_p 905 * pointer accordingly, and then init the buffer with the proper length 906 * and ISNS_BUF_STATIC type. 907 */ 908 pdu_p->payload_p = (struct isns_buffer_s *) 909 isns_buffer_data(buf_p, buf_p->cur_len); 910 ISNS_INIT_BUFFER(pdu_p->payload_p, (unsigned)(buf_p->alloc_len - 911 sizeof(struct isns_pdu_s) - sizeof(struct isns_buffer_s)), 912 ISNS_BUF_STATIC); 913 914 DBG("isns_init_pdu: %p\n", pdu_p); 915 916 return pdu_p; 917 } 918 919 920 /* 921 * isns_add_pdu_payload_data - add data to PDU payload 922 */ 923 static int 924 isns_add_pdu_payload_data(struct isns_trans_s *trans_p, const void *data_p, 925 int len) 926 { 927 struct isns_pdu_s *pdu_p, *new_pdu_p; 928 struct isns_buffer_s *buf_p, *new_buf_p; 929 const uint8_t *src_p; 930 uint8_t *dst_p; 931 int pad_bytes; 932 933 /* Get the request PDU for this transaction. */ 934 if ((pdu_p = isns_get_pdu_request_tail(trans_p)) == NULL) { 935 DBG("isns_add_pdu_payload_data: no request PDU\n"); 936 return EINVAL; 937 } 938 /* Get the active buffer for this PDU (where data should be copied). */ 939 buf_p = isns_get_pdu_active_buffer(pdu_p); 940 941 /* Set up source and destination pointers. Calculate pad bytes. */ 942 src_p = data_p; 943 dst_p = isns_buffer_data(buf_p, buf_p->cur_len); 944 pad_bytes = ISNS_PAD4_BYTES(len); 945 946 /* 947 * Move data from source to PDU buffer(s), allocated new pdus and 948 * buffers as necessary to accomodate the data. 949 */ 950 while (len--) { 951 /* If at max for one PDU payload, add a new PDU. */ 952 if (pdu_p->hdr.payload_len == ISNS_MAX_PDU_PAYLOAD) { 953 new_pdu_p = isns_new_pdu(trans_p->cfg_p, 954 trans_p->id, trans_p->func_id, pdu_p->hdr.flags); 955 if (new_pdu_p == NULL) 956 return ENOMEM; 957 isns_add_pdu_request(trans_p, new_pdu_p); 958 pdu_p = new_pdu_p; 959 buf_p = pdu_p->payload_p; 960 dst_p = isns_buffer_data(buf_p, 0); 961 } 962 /* If at end of current buffer, add a new buffer. */ 963 if (buf_p->cur_len == buf_p->alloc_len) { 964 if (buf_p->next != NULL) 965 buf_p = buf_p->next; 966 else { 967 new_buf_p = isns_new_buffer(0); 968 if (new_buf_p == NULL) 969 return ENOMEM; 970 buf_p->next = new_buf_p; 971 buf_p = new_buf_p; 972 } 973 dst_p = isns_buffer_data(buf_p, 0); 974 } 975 pdu_p->hdr.payload_len++; 976 buf_p->cur_len++; 977 *dst_p++ = *src_p++; 978 } 979 980 /* 981 * Since the buffer alloc length is always a multiple of 4, we are 982 * guaranteed to have enough room for the pad bytes. 983 */ 984 if (pad_bytes > 0) { 985 pdu_p->hdr.payload_len += pad_bytes; 986 buf_p->cur_len += pad_bytes; 987 while (pad_bytes--) 988 *dst_p++ = 0; 989 } 990 991 return 0; 992 } 993 994 995 /* 996 * isns_get_tlv_info_advance - advances pdu/buffer pointers if at end of 997 * current buffer. 998 */ 999 static void 1000 isns_get_tlv_info_advance(struct isns_get_tlv_info_s *info_p) 1001 { 1002 if ((info_p->buf_p != NULL) && 1003 (info_p->buf_ofs == (int)info_p->buf_p->cur_len)) { 1004 info_p->buf_p = info_p->buf_p->next; 1005 info_p->buf_ofs = 0; 1006 } 1007 1008 if ((info_p->buf_p == NULL) && (info_p->pdu_p->next != NULL)) { 1009 info_p->pdu_p = info_p->pdu_p->next; 1010 info_p->buf_p = isns_get_pdu_head_buffer(info_p->pdu_p); 1011 info_p->buf_ofs = 0; 1012 } 1013 } 1014 1015 1016 /* 1017 * isns_get_tlv_uint32 - retrieve host-ordered uint32_t from PDU buffer at 1018 * starting offset and adjusts isns_get_tlv_info 1019 * pointers accordingly. 1020 */ 1021 static int 1022 isns_get_tlv_uint32(struct isns_get_tlv_info_s *info_p, uint32_t *uint32_p) 1023 { 1024 /* Advance to next buffer/pdu (if necessary). */ 1025 isns_get_tlv_info_advance(info_p); 1026 1027 if ((info_p->buf_p == NULL) || 1028 ((info_p->buf_ofs + 4) > (int)info_p->buf_p->cur_len)) { 1029 DBG("isns_get_tlv_uint32: end of buffer reached\n"); 1030 return EFAULT; 1031 } 1032 1033 *uint32_p = ntohl(*(uint32_t *)isns_buffer_data(info_p->buf_p, 1034 info_p->buf_ofs)); 1035 info_p->buf_ofs += 4; 1036 1037 return 0; 1038 } 1039 1040 1041 /* 1042 * isns_get_tlv_data - retrieves data from PDU buffer at starting offset 1043 * for TLV data contained in specified isns_get_tlv_info. 1044 */ 1045 static int 1046 isns_get_tlv_data(struct isns_get_tlv_info_s *info_p, int data_len, 1047 void **data_pp) 1048 { 1049 struct isns_buffer_s *extra_buf_p; 1050 struct isns_get_tlv_info_s gti; 1051 uint8_t *data_p, *extra_data_p; 1052 int bytes_remaining, cbytes; 1053 1054 /* First, NULL return data pointer. */ 1055 *data_pp = NULL; 1056 1057 /* Advance to next buffer/pdu (if necessary). */ 1058 isns_get_tlv_info_advance(info_p); 1059 1060 /* Make sure we have a current get tlv info buffer. */ 1061 if (info_p->buf_p == NULL) { 1062 DBG("isns_get_tlv_data: no next buffer\n"); 1063 return EFAULT; 1064 } 1065 1066 /* Get pointer into buffer where desired TLV resides. */ 1067 data_p = isns_buffer_data(info_p->buf_p, info_p->buf_ofs); 1068 1069 /* TLV data completely resides in current buffer. */ 1070 if ((info_p->buf_ofs + data_len) <= (int)info_p->buf_p->cur_len) { 1071 info_p->buf_ofs += data_len; 1072 *data_pp = data_p; 1073 return 0; 1074 } 1075 1076 /* 1077 * TLV data extends into next buffer so an "extra" buffer is allocated 1078 * that is large enough to hold the entire data value. The extra buffer 1079 * is added to the transaction's extra buffer list so we can free it 1080 * when the transaction is freed. 1081 */ 1082 1083 if ((extra_buf_p = isns_new_buffer(data_len)) == NULL) { 1084 DBG("isns_get_tlv_data: error on isns_new_buffer()\n"); 1085 return ENOMEM; 1086 } 1087 if (info_p->extra_buf_list == NULL) 1088 info_p->extra_buf_list = extra_buf_p; 1089 else { 1090 extra_buf_p->next = info_p->extra_buf_list; 1091 info_p->extra_buf_list = extra_buf_p; 1092 } 1093 1094 /* Setup to copy data bytes out to extra buffer. */ 1095 gti = *info_p; 1096 extra_data_p = isns_buffer_data(extra_buf_p, 0); 1097 bytes_remaining = data_len; 1098 1099 while (bytes_remaining > 0) { 1100 /* 1101 * Advance to next buffer/pdu (if necessary), using local copy 1102 * of the get_tlv_info structure. 1103 */ 1104 isns_get_tlv_info_advance(>i); 1105 if (gti.buf_p == NULL) { 1106 DBG("isns_get_tlv_data: no next buffer\n"); 1107 return EFAULT; 1108 } 1109 1110 data_p = isns_buffer_data(gti.buf_p, gti.buf_ofs); 1111 1112 cbytes = MIN(bytes_remaining, ((int)gti.buf_p->cur_len - gti.buf_ofs)); 1113 bytes_remaining -= cbytes; 1114 gti.buf_ofs += cbytes; 1115 while (cbytes--) 1116 *extra_data_p++ = *data_p++; 1117 } 1118 1119 /* Update isns_get_tlv_info with our local copy. */ 1120 *info_p = gti; 1121 1122 /* Assign return data pointer. */ 1123 *data_pp = isns_buffer_data(extra_buf_p, 0); 1124 1125 return 0; 1126 } 1127 1128 1129 /* 1130 * isns_get_pdu_response_status - returns status in PDU response 1131 * 1132 * Returns: 0 - success 1133 * EPERM - operation not permitted, trans not complete 1134 * EINVAL - invalid trans PDU response/payload 1135 */ 1136 int 1137 isns_get_pdu_response_status(struct isns_trans_s *trans_p, uint32_t *status_p) 1138 { 1139 struct isns_pdu_s *pdu_p; 1140 1141 if ((isns_get_trans_flags(trans_p) & ISNS_TRANSF_COMPLETE) == 0) 1142 return EPERM; 1143 1144 pdu_p = isns_get_pdu_response(trans_p); 1145 if ((pdu_p == NULL) 1146 || (pdu_p->payload_p == NULL) 1147 || (pdu_p->payload_p->cur_len < 4)) 1148 return EINVAL; 1149 1150 *status_p = htonl(*(uint32_t *)isns_buffer_data(pdu_p->payload_p, 0)); 1151 1152 return 0; 1153 } 1154 1155 1156 /* 1157 * isns_add_pdu_list - adds pdu to specified pdu list 1158 */ 1159 static void 1160 isns_add_pdu_list(struct isns_pdu_s **list_pp, struct isns_pdu_s *pdu_p) 1161 { 1162 struct isns_pdu_s *p, *p_prev; 1163 1164 1165 if (*list_pp == NULL) { 1166 *list_pp = pdu_p; 1167 pdu_p->next = NULL; 1168 return; 1169 } 1170 1171 p = *list_pp; 1172 while (p != NULL) { 1173 if (pdu_p->hdr.seq_id < p->hdr.seq_id) { 1174 if (p == *list_pp) { 1175 *list_pp = pdu_p; 1176 pdu_p->next = p; 1177 } else { 1178 p_prev = *list_pp; 1179 while (p_prev->next != p) 1180 p_prev = p_prev->next; 1181 p_prev->next = pdu_p; 1182 pdu_p->next = p; 1183 } 1184 1185 return; 1186 } 1187 p = p->next; 1188 } 1189 1190 /* pdu_p->hdr.seq_id > hdr.seq_id of all list elements */ 1191 p = *list_pp; 1192 while (p->next != NULL) 1193 p = p->next; 1194 p->next = pdu_p; 1195 pdu_p->next = NULL; 1196 } 1197 1198 1199 /* 1200 * isns_get_pdu_head_buffer - returns PDU payload head buffer 1201 */ 1202 static struct isns_buffer_s * 1203 isns_get_pdu_head_buffer(struct isns_pdu_s *pdu_p) 1204 { 1205 return pdu_p->payload_p; 1206 } 1207 1208 1209 #if 0 1210 /* 1211 * isns_get_pdu_tail_buffer - returns PDU payload tail buffer 1212 */ 1213 static struct isns_buffer_s * 1214 isns_get_pdu_tail_buffer(struct isns_pdu_s *pdu_p) 1215 { 1216 struct isns_buffer_s *buf_p; 1217 1218 buf_p = pdu_p->payload_p; 1219 if (buf_p != NULL) 1220 while (buf_p->next != NULL) buf_p = buf_p->next; 1221 1222 return buf_p; 1223 } 1224 #endif 1225 1226 1227 /* 1228 * isns_get_pdu_active_buffer - returns PDU payload "active buffer where the 1229 * next TLV/data should be written 1230 */ 1231 static struct isns_buffer_s * 1232 isns_get_pdu_active_buffer(struct isns_pdu_s *pdu_p) 1233 { 1234 struct isns_buffer_s *buf_p; 1235 1236 buf_p = pdu_p->payload_p; 1237 while ((buf_p->next != NULL) && (buf_p->cur_len == buf_p->alloc_len)) { 1238 buf_p = buf_p->next; 1239 } 1240 1241 return buf_p; 1242 } 1243 1244 1245 /* 1246 * isns_get_next_trans_id - returns next ISNS transaction ID to use 1247 */ 1248 static uint32_t 1249 isns_get_next_trans_id(void) 1250 { 1251 static int trans_id = 1; 1252 1253 return trans_id++; 1254 } 1255 1256 1257 #ifdef ISNS_DEBUG 1258 /* 1259 * isns_dump_pdu - dumps PDU contents 1260 */ 1261 void 1262 isns_dump_pdu(struct isns_pdu_s *pdu_p) 1263 { 1264 int n, pos; 1265 struct isns_buffer_s *buf_p; 1266 uint8_t *p; 1267 char text[17]; 1268 1269 if (pdu_p == NULL) { 1270 DBG("isns_dump_pdu: pdu_p is NULL\n"); 1271 return; 1272 } 1273 1274 DBG("pdu header:\n"); 1275 if (pdu_p->byteorder_host) { 1276 DBG("ver=0x%04X, func=%d(%s), len=%d, flags=0x%04X, trans=%d, " 1277 "seq=%d\n", 1278 pdu_p->hdr.isnsp_version, 1279 pdu_p->hdr.func_id & ~0x8000, 1280 (pdu_p->hdr.func_id & 0x8000 ? "rsp" : "req"), 1281 pdu_p->hdr.payload_len, 1282 pdu_p->hdr.flags, 1283 pdu_p->hdr.trans_id, 1284 pdu_p->hdr.seq_id); 1285 } else { 1286 DBG("ver=0x%04X, func=%d(%s), len=%d, flags=0x%04X, trans=%d, " 1287 "seq=%d\n", 1288 isns_ntohs(pdu_p->hdr.isnsp_version), 1289 isns_ntohs(pdu_p->hdr.func_id) & ~0x8000, 1290 (pdu_p->hdr.func_id & 0x0080 ? "rsp" : "req"), 1291 isns_ntohs(pdu_p->hdr.payload_len), 1292 isns_ntohs(pdu_p->hdr.flags), 1293 isns_ntohs(pdu_p->hdr.trans_id), 1294 isns_ntohs(pdu_p->hdr.seq_id)); 1295 } 1296 1297 DBG("pdu buffers:\n"); 1298 buf_p = pdu_p->payload_p; 1299 while (buf_p != NULL) { 1300 DBG("[%p]: alloc_len=%d, cur_len=%d\n", 1301 buf_p, buf_p->alloc_len, buf_p->cur_len); 1302 buf_p = buf_p->next; 1303 } 1304 1305 DBG("pdu payload:\n"); 1306 buf_p = pdu_p->payload_p; 1307 if (buf_p == NULL) { 1308 DBG("<none>\n"); 1309 return; 1310 } 1311 1312 pos = 0; 1313 memset(text, 0, 17); 1314 while (buf_p != NULL) { 1315 p = isns_buffer_data(buf_p, 0); 1316 for (n = 0; n < buf_p->cur_len; n++) { 1317 DBG("%02X ", *p); 1318 text[pos] = (isprint(*p) ? *p : '.'); 1319 pos++; 1320 p++; 1321 1322 if ((pos % 16) == 0) { 1323 DBG(" %s\n", text); 1324 memset(text, 0, 17); 1325 pos = 0; 1326 } 1327 } 1328 buf_p = buf_p->next; 1329 } 1330 1331 if ((pos % 16) != 0) 1332 DBG("%*c %s\n", (16 - (pos % 16)) * 3, ' ', text); 1333 } 1334 #endif /* ISNS_DEBUG */ 1335