1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 27 #include <errno.h> 28 #include <fcntl.h> 29 #include <libgen.h> 30 #include <netdb.h> 31 #include <procfs.h> 32 #include <pthread.h> 33 #include <stdio.h> 34 #include <stdlib.h> 35 #include <string.h> 36 #include <sys/types.h> 37 #include <ctype.h> 38 #include <dlfcn.h> 39 40 #include "mgmt_acsls.h" 41 #include "mgmt_library.h" 42 #include "mgmt_media.h" 43 #include "mms_cfg.h" 44 45 static char *_SrcFile = __FILE__; /* Using __FILE__ makes duplicate strings */ 46 47 /* 48 * This code represents a ACS client application and communicates with the ACS 49 * library via the ACSAPI interface. ACSAPI procedures communicate via IPC 50 * with the SSI process running on this same client machine. Each client app 51 * can send multiple requests to the ACS Library Manager via this SSI. The SSI 52 * receives requests from one or more clients, places them on a queue, and sends 53 * the requests to the CSI to relay them to the ACS Library Manager. Multiple 54 * heterogeneous clients can communicate and manage the ACSLS Library via the 55 * same SSI. The SSI also relays the responses back to the appropriate client 56 * application. The CSI and SSI talk to each other via RPC. The same RPC program 57 * number is used for all instances of SSI and CSI connections. So there is a 58 * limitation that a client cannot connect to multiple ACSLS. 59 * 60 * The ACSAPI resides on the client machine as a set of three C language library 61 * object modules to be linked with a client application. These modules are the 62 * formal interface, and the functions that carry out the IPC for requests and 63 * responses. 64 * 65 */ 66 67 static int acs_dlsym(void); 68 69 pthread_mutex_t acs_mutex = PTHREAD_MUTEX_INITIALIZER; 70 static boolean_t acs_init = B_FALSE; 71 static STATUS (*dl_acs_display)(SEQ_NO, TYPE, DISPLAY_XML_DATA) = NULL; 72 static STATUS (*dl_acs_response)(int, SEQ_NO *, REQ_ID *, ACS_RESPONSE_TYPE *, 73 ALIGNED_BYTES) = NULL; 74 75 /* Display configuration and status */ 76 static int acs_display_info(int query_type, char *cmdarg, mms_list_t *lst); 77 static int parse_drv_resp(void *buf, mms_list_t *lst); 78 static int parse_lsm_resp(void *buf, mms_list_t *lst); 79 static int parse_vol_resp(void *buf, mms_list_t *vol_list); 80 static int parse_f(char *f, char *s, size_t len); 81 static int parse_f_int(char *f, uint32_t *s); 82 static int parse_f_date(char *f, time_t *t); 83 84 static acs_query_cmdresp_t acs_query_cmdresp_tbl[] = { 85 {ACS_DISPLAY_CAP, ACS_XMLREQ_CAP, NULL}, 86 {ACS_DISPLAY_CELL, ACS_XMLREQ_CELL, NULL}, 87 {ACS_DISPLAY_DRIVE, ACS_XMLREQ_DRIVE, parse_drv_resp}, 88 {ACS_DISPLAY_LOCK, ACS_XMLREQ_LOCK, NULL}, 89 {ACS_DISPLAY_LSM, ACS_XMLREQ_LSM, parse_lsm_resp}, 90 {ACS_DISPLAY_PANEL, ACS_XMLREQ_PANEL, NULL}, 91 {ACS_DISPLAY_POOL, ACS_XMLREQ_POOL, NULL}, 92 {ACS_DISPLAY_VOL, ACS_XMLREQ_VOL, parse_vol_resp}, 93 {ACS_DISPLAY_VOL_BY_MEDIA, ACS_XMLREQ_VOL_BY_MEDIA, NULL}, 94 {ACS_DISPLAY_VOL_CLEANING, ACS_XMLREQ_VOL_CLEANING, NULL}, 95 {ACS_DISPLAY_VOL_ACCESSED, ACS_XMLREQ_VOL_ACCESSED, NULL}, 96 {ACS_DISPLAY_VOL_ENTERED, ACS_XMLREQ_VOL_ENTERED, NULL}, 97 {ACS_DISPLAY_UNSUPPORTED, "", NULL} 98 }; 99 100 101 /* 102 * As ACSLS is a separate product, all functions must be retrieved 103 * using dlopen/dlsym(). Do it once for the duration of the exe. 104 */ 105 static int 106 acs_dlsym(void) 107 { 108 int st = 0; 109 void *hdl = NULL; 110 char buf[2048]; 111 char acspath[2048]; 112 113 st = mms_cfg_getvar(MMS_CFG_LIBAPI_PATH, acspath); 114 if (st != 0) { 115 return (st); 116 } 117 118 (void) pthread_mutex_lock(&acs_mutex); 119 if (!acs_init) { 120 (void) snprintf(buf, sizeof (buf), "%s/%s", 121 acspath, "libapi.so"); 122 hdl = dlopen(buf, RTLD_LAZY); 123 if (hdl == NULL) { 124 /* not there, try the normal locations */ 125 hdl = dlopen("libapi.so", RTLD_LAZY); 126 if (hdl == NULL) { /* still no luck */ 127 (void) pthread_mutex_unlock(&acs_mutex); 128 return (MMS_MGMT_ACSLS_NOT_FOUND); 129 } 130 } 131 dl_acs_display = (STATUS (*)(SEQ_NO, TYPE, DISPLAY_XML_DATA)) 132 dlsym(hdl, "acs_display"); 133 134 dl_acs_response = (STATUS (*)(int, SEQ_NO *, REQ_ID *, 135 ACS_RESPONSE_TYPE *, ALIGNED_BYTES))dlsym(hdl, 136 "acs_response"); 137 138 if ((!dl_acs_display) || (!dl_acs_response)) { 139 st = MMS_MGMT_ACSLS_NOT_FOUND; 140 } else { 141 acs_init = B_TRUE; 142 } 143 } 144 (void) pthread_mutex_unlock(&acs_mutex); 145 146 return (st); 147 } 148 149 /* 150 * Interface to control the lifecycle of the SSI process and its children 151 * 152 * Any priviledged client of ACSLS can start the SSI process. The same process 153 * is to be used by all ACS clients on this machine to communicate within the 154 * ACSLS library. Do not start multiple SSI process. 155 * 156 * The envva ACSAPI_SSI_SOCKET is the local port number of the SSI 157 */ 158 int 159 acs_start_ssi(char *acshost, char *ssiport) 160 { 161 int st; 162 mms_list_t proclist; 163 pid_t pid; 164 char env_acshost[NI_MAXHOST + 13]; /* CSI_HOSTNAME=<hostname> */ 165 char env_acsport[128]; 166 int status; 167 char acspath[2048]; 168 char ssibuf[1024]; 169 char sockbuf[1024]; 170 char *cmd[3]; 171 char *hostport; 172 173 if (acshost == NULL) { 174 return (MMS_MGMT_NOARG); 175 } 176 177 st = mms_cfg_getvar(MMS_CFG_SSI_PATH, acspath); 178 if (st != 0) { 179 return (st); 180 } 181 182 (void) snprintf(ssibuf, sizeof (ssibuf), "%s/%s", acspath, "ssi"); 183 if (find_process(ssibuf, &proclist) == 0) { 184 if (!mms_list_empty(&proclist)) { 185 mms_list_free_and_destroy(&proclist, free); 186 return (0); 187 } 188 } 189 190 (void) snprintf(env_acshost, sizeof (env_acshost), "CSI_HOSTNAME=%s", 191 acshost); 192 hostport = strrchr(env_acshost, ':'); 193 if (hostport != NULL) { 194 *hostport = '\0'; 195 hostport++; 196 /* don't set if we're using the default port */ 197 if (strcmp(hostport, "50004") == 0) { 198 hostport = NULL; 199 } 200 } 201 202 if (hostport != NULL) { 203 (void) snprintf(env_acsport, sizeof (env_acsport), 204 "CSI_HOSTPORT=%s", hostport); 205 } 206 207 (void) snprintf(ssibuf, sizeof (ssibuf), "MMS_SSI_PATH=%s", acspath); 208 209 if (ssiport != NULL) { 210 (void) snprintf(sockbuf, sizeof (sockbuf), 211 "ACSAPI_SSI_SOCKET=%s", ssiport); 212 } else { 213 (void) snprintf(sockbuf, sizeof (sockbuf), 214 "ACSAPI_SSI_SOCKET=%s", "50004"); 215 } 216 217 /* set required envvars */ 218 (void) putenv(env_acshost); 219 if (hostport != NULL) { 220 (void) putenv(env_acsport); 221 } 222 (void) putenv(sockbuf); 223 (void) putenv(ssibuf); 224 225 cmd[0] = "/usr/bin/mmsssi.sh"; 226 cmd[1] = "1"; 227 cmd[2] = NULL; 228 229 pid = exec_mgmt_cmd(NULL, NULL, 0, 0, B_TRUE, cmd); 230 mms_trace(MMS_DEBUG, "exec_mgmt_cmd: %s %s", cmd[0], cmd[1]); 231 232 status = check_exit(pid, NULL); 233 234 if (status != 0) { 235 mms_trace(MMS_ERR, 236 "Could not start ACSLS client daemon, exec status = %d", 237 status); 238 } 239 240 return (status); 241 } 242 243 244 /* 245 * get the configuration of the acs library, given the name of the acsls 246 * hostname and port. 247 * 248 * If get_drives is TRUE, get information about drives as well as libraries 249 */ 250 int 251 get_acs_library_cfg( 252 char *acshost, 253 boolean_t get_drives, 254 mms_list_t *lsm_list 255 ) 256 { 257 int st; 258 char location[128]; 259 mms_acslib_t *lsm = NULL; 260 261 if ((acshost == NULL) || (lsm_list == NULL)) { 262 return (MMS_MGMT_NOARG); 263 } 264 265 mms_list_create(lsm_list, sizeof (mms_acslib_t), 266 offsetof(mms_acslib_t, lib_link)); 267 268 /* check if the acsls host is accessible and start if it not */ 269 if (acs_start_ssi(acshost, NULL) != 0) { 270 return (MMS_MGMT_ERR_EXEC_SSI); 271 } 272 273 /* get all the acs-lsm */ 274 st = acs_display_info(ACS_DISPLAY_LSM, NULL, lsm_list); 275 if (st != 0) { 276 return (st); 277 } 278 279 if (get_drives) { 280 mms_list_foreach(lsm_list, lsm) { 281 /* get the drives in each library */ 282 (void) snprintf(location, sizeof (location), 283 "%d,%d,*,*", lsm->acs, lsm->lsm); 284 285 st = acs_display_info(ACS_DISPLAY_DRIVE, location, 286 &lsm->drive_list); 287 if (st != 0) { 288 break; 289 } 290 } 291 } 292 293 return (st); 294 } 295 296 static int 297 wait_for_response( 298 int seq, 299 int (*parse_acs_resp)(void *, mms_list_t *), 300 mms_list_t *lst) 301 { 302 303 STATUS st; 304 SEQ_NO rseq; 305 REQ_ID reqid; 306 int ret; 307 ALIGNED_BYTES rbuf[MAX_MESSAGE_SIZE / sizeof (ALIGNED_BYTES)]; 308 ACS_RESPONSE_TYPE type; 309 310 if ((parse_acs_resp == NULL) || (lst == NULL)) { 311 return (MMS_MGMT_NOARG); 312 } 313 314 /* 315 * call acs_response() repeatedly until the FINAL packet for this 316 * request has been received 317 */ 318 do { 319 (void) memset(rbuf, 0, sizeof (rbuf)); 320 321 st = dl_acs_response( 322 10, /* Block for 10 seconds */ 323 &rseq, 324 &reqid, 325 &type, 326 rbuf); 327 328 if (st == STATUS_IPC_FAILURE) { 329 return (MMS_MGMT_ERR_ACSLS_RSP); 330 } 331 332 if (rseq != seq) { 333 mms_trace(MMS_ERR, "Invalid ACS Sequence number, %d", 334 rseq); 335 return (MMS_MGMT_ERR_ACSLS_RSP); 336 } 337 338 if ((type == RT_INTERMEDIATE) || (type == RT_FINAL)) { 339 ret = parse_acs_resp(rbuf, lst); 340 if (ret != 0) { 341 ret = MMS_MGMT_ERR_ACSLS_PARSE; 342 break; 343 } 344 } 345 } while (type != RT_FINAL); 346 347 return (ret); 348 } 349 350 351 /* 352 * To get the configuration of the components in an ACSLS library, or their 353 * status, use the 'display' command to create complex or detailed queries 354 * using XML as the Query language. The XML request is then sent to the SSI 355 * using the acs_display() ACSAPI and the responses are awaited and parsed. 356 * 357 * The SSI process must be running before this API can be used. 358 */ 359 int 360 acs_display_info( 361 int query_type, /* type of query */ 362 char *cmdarg, /* arguments for the XML request */ 363 mms_list_t *lst) /* response parsed as a list */ 364 { 365 366 int st = 0; 367 SEQ_NO seq; 368 DISPLAY_XML_DATA cmd; 369 char *s = "*"; 370 size_t len; 371 372 if (lst == NULL) { 373 return (MMS_MGMT_NOARG); 374 } 375 376 st = acs_dlsym(); 377 if (st != 0) { 378 return (st); 379 } 380 381 if (cmdarg && (strlen(cmdarg) > 0)) { 382 s = cmdarg; 383 } 384 385 /* LINTED [E_SEC_PRINTF_VAR_FMT] */ 386 len = snprintf(cmd.xml_data, sizeof (cmd.xml_data), 387 acs_query_cmdresp_tbl[query_type].xmlreq, s); 388 mms_trace(MMS_INFO, "DISPLAY cmd:\n%s", cmd.xml_data); 389 cmd.length = strlen(cmd.xml_data); 390 391 if (len > MAX_XML_DATA_SIZE) { 392 return (ENAMETOOLONG); 393 } 394 395 /* 396 * generate a sequence number, this uniquely identifies the response 397 * with the request. SEQ_NO is defined as a short int. 398 */ 399 seq = (SEQ_NO)(time(NULL)); 400 401 if ((dl_acs_display(seq, TYPE_DISPLAY, cmd)) != STATUS_SUCCESS) { 402 return (MMS_MGMT_ERR_ACSLS_PARSE); 403 } 404 405 st = wait_for_response(seq, (int (*)(void *, mms_list_t *)) 406 acs_query_cmdresp_tbl[query_type].parse_resp, lst); 407 408 if (st != 0) { 409 mms_trace(MMS_INFO, "get acs display info failed %d", st); 410 return (st); 411 } else { 412 mms_trace(MMS_INFO, "get acs display info success"); 413 } 414 415 return (st); 416 } 417 418 419 /* 420 * parse_drive_resp() assumes the format of the data response, the 421 * following information is expected in the drive data: 422 * acs, lsm, panel, drive, type, status, state and serial number 423 */ 424 static int 425 parse_drv_resp( 426 void *buf, 427 mms_list_t *drive_list) 428 { 429 430 ACS_DISPLAY_RESPONSE *res; 431 size_t l; 432 char *ptr1, *ptr2; 433 mms_drive_t *drive; 434 char junkbuf[1024]; 435 char xml_buf[MAX_MESSAGE_SIZE + 1]; 436 int len; 437 438 if ((buf == NULL) || (drive_list == NULL)) { 439 return (MMS_MGMT_NOARG); 440 } 441 442 res = (ACS_DISPLAY_RESPONSE *)buf; 443 if (res->display_status != STATUS_SUCCESS) { 444 return (MMS_MGMT_ERR_ACSLS_RSP); 445 } 446 447 len = res->display_xml_data.length; 448 (void) strncpy(xml_buf, res->display_xml_data.xml_data, 449 len); 450 xml_buf[len] = '\0'; 451 mms_trace(MMS_INFO, "Display DRV response len = %d :\n%s", 452 len, xml_buf); 453 ptr1 = xml_buf; 454 455 ptr2 = strstr(ptr1, "</data></display></response>"); 456 if (ptr2 != NULL) { 457 *ptr2 = NULL; 458 }; 459 460 /* 461 * <r> marks the start of a drive entry, <f> marks the start of a field 462 * 463 * <r> 464 * <f maxlen="3">acs</f> 465 * <f maxlen="3">lsm</f> 466 * <f maxlen="5">panel</f> 467 * <f maxlen="5">drive</f> 468 * <f maxlen="9">status</f> 469 * <f maxlen="10">state</f> 470 * <f maxlen="6">volume</f> 471 * <f maxlen="9">type</f> 472 * <f maxlen="5">lock</f> 473 * <f maxlen="32">serial_num</f> 474 * <f maxlen="14">condition</f> 475 * </r> 476 */ 477 478 if ((ptr2 = strstr(ptr1, "<data>")) != NULL) { 479 480 if (drive_list->list_size == 0) { 481 mms_list_create(drive_list, sizeof (mms_drive_t), 482 offsetof(mms_drive_t, drive_link)); 483 } 484 485 while ((ptr2 = strstr(ptr1, "<r>")) != NULL) { 486 487 drive = calloc(1, sizeof (mms_drive_t)); 488 if (drive == NULL) { 489 return (ENOMEM); 490 } 491 492 /* extract string from <f ....>..</f> */ 493 ptr2 += 3; /* skip past <r> */ 494 l = parse_f_int(ptr2, &drive->acs); 495 ptr2 += l; 496 l = parse_f_int(ptr2, &drive->lsm); 497 ptr2 += l; 498 l = parse_f_int(ptr2, &drive->panel); 499 ptr2 += l; 500 l = parse_f_int(ptr2, &drive->drive); 501 ptr2 += l; 502 /* properly convert flags and provide for volume */ 503 /* status */ 504 l = parse_f(ptr2, junkbuf, sizeof (junkbuf)); 505 ptr2 += l; 506 /* state */ 507 l = parse_f(ptr2, junkbuf, sizeof (junkbuf)); 508 ptr2 += l; 509 /* volume */ 510 l = parse_f(ptr2, drive->volid, sizeof (drive->volid)); 511 ptr2 += l; 512 l = parse_f(ptr2, drive->type, sizeof (drive->type)); 513 ptr2 += l; 514 /* lock */ 515 l = parse_f(ptr2, junkbuf, sizeof (junkbuf)); 516 ptr2 += l; 517 l = parse_f(ptr2, drive->serialnum, 518 sizeof (drive->serialnum)); 519 ptr2 += l; 520 /* condition */ 521 l = parse_f(ptr2, junkbuf, sizeof (junkbuf)); 522 ptr2 += l; 523 524 mms_list_insert_tail(drive_list, drive); 525 526 ptr2 += 4; /* advance to the start of the next drive */ 527 ptr1 = ptr2; 528 } 529 } 530 return (0); 531 } 532 533 534 /* 535 * parse_vol_resp() assumes the format of the data response, the 536 * following information is expected in the volume data: 537 * vol_id, acs, lsm, panel, row, column, pool, status, media, and type 538 */ 539 static int 540 parse_vol_resp(void *buf, mms_list_t *vol_list) 541 { 542 543 ACS_DISPLAY_RESPONSE *res; 544 size_t l; 545 char *ptr1, *ptr2; 546 mms_acscart_t *vol; 547 char junkbuf[1024]; 548 char xml_buf[MAX_MESSAGE_SIZE + 1]; 549 int len; 550 551 if ((buf == NULL) || (vol_list == NULL)) { 552 return (MMS_MGMT_NOARG); 553 } 554 555 res = (ACS_DISPLAY_RESPONSE *)buf; 556 if (res->display_status != STATUS_SUCCESS) { 557 return (MMS_MGMT_ERR_ACSLS_RSP); 558 } 559 560 len = res->display_xml_data.length; 561 (void) strncpy(xml_buf, res->display_xml_data.xml_data, 562 len); 563 xml_buf[len] = '\0'; 564 mms_trace(MMS_INFO, "Display VOL response len = %d :\n%s", 565 len, xml_buf); 566 ptr1 = xml_buf; 567 568 ptr2 = strstr(ptr1, "</data></display></response>"); 569 if (ptr2 != NULL) { 570 *ptr2 = NULL; 571 }; 572 if ((ptr2 = strstr(ptr1, "<data>")) != NULL) { 573 574 /* only create the list if it's the first time through */ 575 if (vol_list->list_size == 0) { 576 mms_list_create(vol_list, sizeof (mms_acscart_t), 577 offsetof(mms_acscart_t, next)); 578 } 579 580 while ((ptr2 = strstr(ptr1, "<r>")) != NULL) { 581 vol = calloc(1, sizeof (mms_acscart_t)); 582 if (vol == NULL) { 583 return (ENOMEM); 584 } 585 /* extract string from <f ....>..</f> */ 586 ptr2 += 3; /* skip past <r> */ 587 588 l = parse_f(ptr2, vol->label, sizeof (vol->label)); 589 ptr2 += l; 590 l = parse_f_int(ptr2, (uint32_t *)&vol->libacs); 591 ptr2 += l; 592 l = parse_f_int(ptr2, (uint32_t *)&vol->liblsm); 593 ptr2 += l; 594 /* drive */ 595 l = parse_f(ptr2, junkbuf, sizeof (junkbuf)); 596 ptr2 += l; 597 /* type - cleaning|data */ 598 l = parse_f(ptr2, junkbuf, sizeof (junkbuf)); 599 ptr2 += l; 600 l = parse_f(ptr2, vol->mtype, sizeof (vol->mtype)); 601 ptr2 += l; 602 /* status */ 603 l = parse_f(ptr2, junkbuf, sizeof (junkbuf)); 604 ptr2 += l; 605 l = parse_f_date(ptr2, &vol->access); 606 ptr2 += l; 607 608 mms_list_insert_tail(vol_list, vol); 609 610 /* advance to the start of the next volume */ 611 ptr2 = strstr(ptr2, "</r>"); 612 if (ptr2 == NULL) { 613 /* malformed response */ 614 break; 615 } 616 ptr1 = ptr2 + 4; 617 } 618 } 619 return (0); 620 } 621 622 623 /* 624 * parse_lsm_resp() parses the response data assuming a particular 625 * format for the data. The following information is expected in the 626 * response:- acs, lsm, status, state and serial number 627 * 628 */ 629 static int 630 parse_lsm_resp( 631 void *buf, 632 mms_list_t *lsm_list) 633 { 634 ACS_DISPLAY_RESPONSE *res; 635 size_t l; 636 char *ptr1, *ptr2; 637 mms_acslib_t *lsm; 638 char status[1024]; 639 char state[1024]; 640 char xml_buf[MAX_MESSAGE_SIZE + 1]; 641 int len; 642 643 if ((buf == NULL) || (lsm_list == NULL)) { 644 return (MMS_MGMT_NOARG); 645 } 646 647 res = (ACS_DISPLAY_RESPONSE *)buf; 648 if (res->display_status != STATUS_SUCCESS) { 649 return (MMS_MGMT_ERR_ACSLS_RSP); 650 } 651 652 len = res->display_xml_data.length; 653 (void) strncpy(xml_buf, res->display_xml_data.xml_data, 654 len); 655 xml_buf[len] = '\0'; 656 mms_trace(MMS_INFO, "Display LSM response len = %d :\n%s", 657 len, xml_buf); 658 ptr1 = xml_buf; 659 660 ptr2 = strstr(ptr1, "</data></display></response>"); 661 if (ptr2 != NULL) { 662 *ptr2 = NULL; 663 }; 664 665 if ((ptr2 = strstr(ptr1, "<data>")) != NULL) { 666 if (lsm_list->list_size == 0) { 667 mms_list_create(lsm_list, sizeof (mms_acslib_t), 668 offsetof(mms_acslib_t, lib_link)); 669 } 670 671 while ((ptr2 = strstr(ptr1, "<r>")) != NULL) { 672 673 lsm = calloc(1, sizeof (mms_acslib_t)); 674 if (lsm == NULL) { 675 return (ENOMEM); 676 } 677 678 ptr2 += 3; /* skip past <r> */ 679 l = parse_f_int(ptr2, &lsm->acs); 680 ptr2 += l; 681 l = parse_f_int(ptr2, &lsm->lsm); 682 ptr2 += l; 683 /* parse status and state to flags */ 684 l = parse_f(ptr2, status, sizeof (status)); 685 ptr2 += l; 686 l = parse_f(ptr2, state, sizeof (state)); 687 ptr2 += l; 688 l = parse_f(ptr2, lsm->serialnum, 689 sizeof (lsm->serialnum)); 690 ptr2 += l; 691 l = parse_f(ptr2, lsm->type, sizeof (lsm->type)); 692 ptr2 += l; 693 694 mms_list_insert_tail(lsm_list, lsm); 695 696 /* advance to the start of the next drive */ 697 ptr2 = strstr(ptr2, "</r>"); 698 if (ptr2 == NULL) { 699 /* malformed response */ 700 break; 701 } 702 ptr1 = ptr2 + 4; 703 } 704 } 705 return (0); 706 } 707 708 static int /* return number of characters parsed */ 709 parse_f(char *f, char *s, size_t len) { 710 711 size_t n; 712 char *ptr; 713 714 if (f == NULL || strlen(f) == 0) { 715 return (0); 716 } 717 718 ptr = strstr(f, "</f>"); 719 if (ptr != NULL) { 720 *ptr = '\0'; 721 } 722 n = strlen(f); 723 724 ptr = strchr(f, '>'); 725 if (ptr == NULL) { 726 return (0); 727 } 728 729 ptr++; 730 731 (void) strlcpy(s, ptr, len); 732 733 return (n + 4); 734 } 735 736 /* parse just a single char */ 737 static int 738 parse_f_int(char *f, uint32_t *i) 739 { 740 char *ptr; 741 char *ptr2; 742 size_t n; 743 char buf[4]; 744 int j; 745 746 if (!f || !i) { 747 return (0); 748 } 749 750 ptr = strchr(f, '>'); 751 if (ptr == NULL) { 752 return (0); 753 } 754 755 ptr++; 756 757 for (j = 0; j < 4; j++, ptr++) { 758 if (!isdigit(*ptr)) { 759 break; 760 } 761 buf[j] = *ptr; 762 } 763 buf[j] = '\0'; 764 ptr2 = strchr(ptr, '>'); 765 if (ptr2 == NULL) { 766 return (0); 767 } 768 n = (++ptr2 - f); 769 770 *i = atoi(buf); 771 772 return (n); 773 } 774 775 static int 776 parse_f_date(char *f, time_t *t) 777 { 778 struct tm tm; 779 char *ptr; 780 size_t n; 781 782 if (!f || !t) { 783 return (0); 784 } 785 786 ptr = strchr(f, '>'); 787 if (ptr == NULL) { 788 return (0); 789 } 790 791 ptr++; 792 793 ptr = strptime(ptr, "%Y-%m-%d %T", &tm); 794 795 ptr = strstr(ptr, "</f>"); 796 if (ptr == NULL) { 797 return (0); 798 } 799 ptr += 4; 800 801 n = ptr - f; 802 803 *t = mktime(&tm); 804 return (n); 805 } 806 807 /* 808 * get volumes from an acs library, given the name of the acsls 809 * hostname and port. 810 * 811 * in_vols is optional, allows the requester to ask for only 812 * those volumes he/she is interested in. 813 * 814 */ 815 int 816 get_acs_volumes( 817 char *acshost, 818 char *in_vols, 819 mms_list_t *vol_list 820 ) 821 { 822 int st; 823 824 if ((acshost == NULL) || (vol_list == NULL)) { 825 return (MMS_MGMT_NOARG); 826 } 827 828 /* check if the acsls host is accessible and start if it not */ 829 if (acs_start_ssi(acshost, NULL) != 0) { 830 return (MMS_MGMT_ERR_EXEC_SSI); 831 } 832 833 st = acs_display_info(ACS_DISPLAY_VOL, in_vols, vol_list); 834 if (st != 0) { 835 return (st); 836 } 837 838 return (st); 839 } 840