Home | History | Annotate | Download | only in common
      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