Home | History | Annotate | Download | only in rds
      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 /*
     23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     28 
     29 #include <search.h>
     30 #include <stdlib.h>
     31 
     32 #include <sys/utsname.h>
     33 #include "rdprot.h"
     34 #include "rdutil.h"
     35 /*
     36  * This file works out the protocol layer of the bidirectional data interface
     37  * between the rds and the client. In the server mode rds writes greetings and
     38  * a protocol header to the output stream.
     39  * pheader  == { "@RDS-MAG@"  PROTV }
     40  * PROTV    == { protocol version }
     41  * Then it sends a prompt and waits for command from client.
     42  * PROMPT   == { "@RDS@>" }
     43  * COMMAND  == { "command"  cmd }
     44  * cmd	    == { "-pUuJjS" | "-p" | "-u" | "-U" |
     45  *		 "-j" | "-J" | "-S" | "-i100" | "alive"| "exit" }
     46  * The answer from rds is always a lists of data. The header of the answer data
     47  * contains the number of lists in the package. Each list has a header and
     48  * some elements, which have again a header and some fields of data:
     49  * answer   == { lshead,  n * list }
     50  * lshead   == { number of lists }
     51  * list     == { lheader, n * element }
     52  * lheader  == { LISTT, ELEMN }
     53  * LISTT    == { type of the list }
     54  * ELEMN    == { number of elements in the list }
     55  * element  == { eheader, field }
     56  * eheader  == { ELMID, FILDN }
     57  * ELMID    == { element id, like pid, uid, project name }
     58  * field    == { KEY, VALUE }
     59  * All protocol elements have a key and a value separated by one space.
     60  * The value begins after the first space and ends with the new line character.
     61  * Protocol keys are: "@RDS-MAG@", PROTV, LISTN,  LISTT, ELEMN ELMID, FILDN,
     62  * RDERR. The special key RDERR can occur in any line and indicates that an
     63  * error condition occurred, where the VALUE is the error message.
     64  */
     65 
     66 static char line[P_MAXLEN];
     67 static char error[P_MAXLEN];
     68 static char value[P_MAXVAL];
     69 static char key[P_MAXKEY];
     70 
     71 static char *nullstr = "";
     72 static FILE *wstream, *rstream;
     73 
     74 static int format_int64(int, char *, char *, int);
     75 static int format_int32(int, char *, char *, int);
     76 static int format_ulong(int, char *, char *, int);
     77 static int format_float(int, char *, char *, int);
     78 static int format_double(int, char *, char *, int);
     79 static int format_string(int, char *, char *, int);
     80 static int format_timestruc(int, char *, char *, int);
     81 
     82 /*
     83  * The kv_pair_t represents an field in a  c-sturcture. An filed
     84  * is defined by a key 'field name', format function and an offset
     85  * in this structure
     86  */
     87 
     88 /*
     89  * Array of fields from id_info_t structure, that are sent/received
     90  * in a process/user/project utilization list.
     91  */
     92 static kv_pair_t id_stub[] =
     93 {
     94 { "id_pid",	{ format_int32, offsetof(id_info_t, id_pid) }},
     95 { "id_uid",	{ format_int32, offsetof(id_info_t, id_uid) }},
     96 { "id_projid",	{ format_int32, offsetof(id_info_t, id_projid) }},
     97 { "id_usr", 	{ format_double, offsetof(id_info_t, id_usr) }},
     98 { "id_sys", 	{ format_double, offsetof(id_info_t, id_sys) }},
     99 { "id_ttime", 	{ format_double, offsetof(id_info_t, id_ttime) }},
    100 { "id_tpftime", { format_double, offsetof(id_info_t, id_tpftime) }},
    101 { "id_dpftime", { format_double, offsetof(id_info_t, id_dpftime) }},
    102 { "id_kpftime", { format_double, offsetof(id_info_t, id_kpftime) }},
    103 { "id_lck", 	{ format_double, offsetof(id_info_t, id_lck) }},
    104 { "id_slp", 	{ format_double, offsetof(id_info_t, id_slp) }},
    105 { "id_lat", 	{ format_double, offsetof(id_info_t, id_lat) }},
    106 { "id_stime", 	{ format_double, offsetof(id_info_t, id_stime) }},
    107 { "id_minf", 	{ format_int64, offsetof(id_info_t, id_minf) }},
    108 { "id_majf", 	{ format_int64, offsetof(id_info_t, id_majf) }},
    109 { "id_nswap", 	{ format_int64, offsetof(id_info_t, id_nswap) }},
    110 { "id_inblk", 	{ format_int64, offsetof(id_info_t, id_inblk) }},
    111 { "id_oublk", 	{ format_int64, offsetof(id_info_t, id_oublk) }},
    112 { "id_msnd", 	{ format_int64, offsetof(id_info_t, id_msnd) }},
    113 { "id_mrcv", 	{ format_int64, offsetof(id_info_t, id_mrcv) }},
    114 { "id_sigs", 	{ format_int64, offsetof(id_info_t, id_sigs) }},
    115 { "id_vctx", 	{ format_int64, offsetof(id_info_t, id_vctx) }},
    116 { "id_ictx", 	{ format_int64, offsetof(id_info_t, id_ictx) }},
    117 { "id_scl", 	{ format_int64, offsetof(id_info_t, id_scl) }},
    118 { "id_ioch", 	{ format_int64, offsetof(id_info_t, id_ioch) }},
    119 { "id_hpsize", 	{ format_int64, offsetof(id_info_t, id_hpsize) }},
    120 { "id_size", 	{ format_int64, offsetof(id_info_t, id_size) }},
    121 { "id_rssize", 	{ format_int64, offsetof(id_info_t, id_rssize) }},
    122 { "id_pctcpu", 	{ format_float, offsetof(id_info_t, id_pctcpu) }},
    123 { "id_pctmem", 	{ format_float, offsetof(id_info_t, id_pctmem) }},
    124 { "id_time", 	{ format_int64, offsetof(id_info_t, id_time) }},
    125 { "id_nlwps", 	{ format_int32, offsetof(id_info_t, id_nlwps) }},
    126 { "id_timestamp", { format_int64, offsetof(id_info_t, id_timestamp) }},
    127 { "id_nproc", 	{ format_int32, offsetof(id_info_t, id_nproc) }},
    128 { "id_inpkg", 	{ format_int64, offsetof(id_info_t, id_inpkg) }},
    129 { "id_oupkg", 	{ format_int64, offsetof(id_info_t, id_oupkg) }},
    130 { "id_name", 	{ format_string, offsetof(id_info_t, id_name) }}
    131 };
    132 
    133 static kv_pair_t lwp_stub[] =
    134 {
    135 {"li_usage",	{ format_ulong, offsetof(lwp_info_t, li_usr) }},
    136 {"li_usr",	{ format_ulong, offsetof(lwp_info_t, li_usr) }},
    137 {"li_sys",	{ format_ulong, offsetof(lwp_info_t, li_sys) }},
    138 {"li_ttime",	{ format_ulong, offsetof(lwp_info_t, li_ttime) }},
    139 {"li_tpftime",	{ format_ulong, offsetof(lwp_info_t, li_tpftime) }},
    140 {"li_dpftime",	{ format_ulong, offsetof(lwp_info_t, li_dpftime) }},
    141 {"li_kpftime",	{ format_ulong, offsetof(lwp_info_t, li_kpftime) }},
    142 {"li_lck",	{ format_ulong, offsetof(lwp_info_t, li_lck) }},
    143 {"li_slp",	{ format_ulong, offsetof(lwp_info_t, li_slp) }},
    144 {"li_lat",	{ format_ulong, offsetof(lwp_info_t, li_lat) }},
    145 {"li_stime",	{ format_ulong, offsetof(lwp_info_t, li_stime) }},
    146 {"li_minf",	{ format_ulong, offsetof(lwp_info_t, li_minf) }},
    147 {"li_majf",	{ format_ulong, offsetof(lwp_info_t, li_majf) }},
    148 {"li_nswap",	{ format_ulong, offsetof(lwp_info_t, li_nswap) }},
    149 {"li_inblk",	{ format_ulong, offsetof(lwp_info_t, li_inblk) }},
    150 {"li_oublk",	{ format_ulong, offsetof(lwp_info_t, li_oublk) }},
    151 {"li_msnd",	{ format_ulong, offsetof(lwp_info_t, li_msnd) }},
    152 {"li_mrcv",	{ format_ulong, offsetof(lwp_info_t, li_mrcv) }},
    153 {"li_sigs",	{ format_ulong, offsetof(lwp_info_t, li_sigs) }},
    154 {"li_vctx",	{ format_ulong, offsetof(lwp_info_t, li_vctx) }},
    155 {"li_ictx",	{ format_ulong, offsetof(lwp_info_t, li_ictx) }},
    156 {"li_scl",	{ format_ulong, offsetof(lwp_info_t, li_scl) }},
    157 {"li_ioch",	{ format_ulong, offsetof(lwp_info_t, li_ioch) }},
    158 {"li_hpsize",	{ format_ulong, offsetof(lwp_info_t, li_hpsize) }},
    159 {"li_timestamp", { format_ulong, offsetof(lwp_info_t, li_timestamp) }},
    160 };
    161 
    162 static kv_pair_t lwpinfo_stub[] =
    163 {
    164 {"lwpr_pid",	{ format_int32, offsetof(lwpinfo_t, pr_pid) }},
    165 {"lwpr_lwpid",	{ format_int32, offsetof(lwpinfo_t, pr_lwpid) }},
    166 };
    167 
    168 static kv_pair_t prusage_stub[] =
    169 {
    170 {"pr_tstamp",	{ format_timestruc, offsetof(prusage_t, pr_tstamp) }},
    171 {"pr_create",	{ format_timestruc, offsetof(prusage_t, pr_create) }},
    172 {"pr_term",	{ format_timestruc, offsetof(prusage_t, pr_term) }},
    173 {"pr_rtime",	{ format_timestruc, offsetof(prusage_t, pr_rtime) }},
    174 {"pr_utime",	{ format_timestruc, offsetof(prusage_t, pr_utime) }},
    175 {"pr_stime",	{ format_timestruc, offsetof(prusage_t, pr_stime) }},
    176 {"pr_ttime",	{ format_timestruc, offsetof(prusage_t, pr_ttime) }},
    177 {"pr_tftime",	{ format_timestruc, offsetof(prusage_t, pr_tftime) }},
    178 {"pr_dftime",	{ format_timestruc, offsetof(prusage_t, pr_dftime) }},
    179 {"pr_kftime",	{ format_timestruc, offsetof(prusage_t, pr_kftime) }},
    180 {"pr_ltime",	{ format_timestruc, offsetof(prusage_t, pr_ltime) }},
    181 {"pr_slptime",	{ format_timestruc, offsetof(prusage_t, pr_slptime) }},
    182 {"pr_wtime",	{ format_timestruc, offsetof(prusage_t, pr_wtime) }},
    183 {"pr_stoptime", { format_timestruc, offsetof(prusage_t, pr_stoptime) }},
    184 {"pr_minf",	{ format_ulong, offsetof(prusage_t, pr_minf) }},
    185 {"pr_majf",	{ format_ulong, offsetof(prusage_t, pr_majf) }},
    186 {"pr_nswap",	{ format_ulong, offsetof(prusage_t, pr_nswap) }},
    187 {"pr_inblk",	{ format_ulong, offsetof(prusage_t, pr_inblk) }},
    188 {"pr_oublk",	{ format_ulong, offsetof(prusage_t, pr_oublk) }},
    189 {"pr_msnd",	{ format_ulong, offsetof(prusage_t, pr_msnd) }},
    190 {"pr_mrcv",	{ format_ulong, offsetof(prusage_t, pr_mrcv) }},
    191 {"pr_sigs",	{ format_ulong, offsetof(prusage_t, pr_sigs) }},
    192 {"pr_vctx",	{ format_ulong, offsetof(prusage_t, pr_vctx) }},
    193 {"pr_ictx",	{ format_ulong, offsetof(prusage_t, pr_ictx) }},
    194 {"pr_sysc",	{ format_ulong, offsetof(prusage_t, pr_sysc) }},
    195 {"pr_ioch",	{ format_ulong, offsetof(prusage_t, pr_ioch) }},
    196 };
    197 
    198 /*
    199  * Array of fields in id_info_t structure, that are sent/received
    200  * in an active user list.
    201  */
    202 static kv_pair_t usr_stub[] =
    203 {
    204 { "usr_id", 	{ format_int32, offsetof(id_info_t, id_uid) }},
    205 { "usr_name", 	{ format_string, offsetof(id_info_t, id_name) }}
    206 };
    207 
    208 /*
    209  * Array of fields in id_info_t structure, that are sent/received
    210  * in an active project list.
    211  */
    212 static kv_pair_t prj_stub[] =
    213 {
    214 { "prj_id", 	{ format_int32, offsetof(id_info_t, id_projid) }},
    215 { "prj_name", 	{ format_string, offsetof(id_info_t, id_name)   }}
    216 };
    217 
    218 /*
    219  * Array of fields in id_info_t structure, that are sent/received
    220  * in a system list.
    221  */
    222 static kv_pair_t sys_stub[] =
    223 {
    224 { "sys_nodename", { format_string, offsetof(sys_info_t, nodename) }},
    225 { "sys_name",	{ format_string, offsetof(sys_info_t, name) }}
    226 };
    227 
    228 /*
    229  * Array of fields in id_info_t structure, that are sent/received
    230  * in command.
    231  */
    232 static kv_pair_t cmd_stub[] =
    233 {
    234 { "command",	{ format_int32, offsetof(cmd_t, command) }}
    235 };
    236 
    237 #define	stubsize(stub) ((sizeof (stub))/(sizeof (kv_pair_t)))
    238 
    239 /*
    240  * Each list type has its own fields description, the list type is
    241  * the index into this table:
    242  * L_PRC_SI - processes statistical information
    243  * L_USR_SI - useres statistical information
    244  * L_PRJ_SI - projects statistical information
    245  * L_AC_USR - active users
    246  * L_AC_PRJ - active projects
    247  * L_SYSTEM - system
    248  */
    249 #define	NOF_STUBS   10
    250 static stub_t stubs[NOF_STUBS + 1] = {
    251 { 0, NULL},
    252 { stubsize(id_stub), id_stub},
    253 { stubsize(id_stub), id_stub},
    254 { stubsize(id_stub), id_stub},
    255 { stubsize(usr_stub), usr_stub},
    256 { stubsize(prj_stub), prj_stub},
    257 { stubsize(sys_stub), sys_stub},
    258 { stubsize(cmd_stub), cmd_stub},
    259 { stubsize(lwp_stub), lwp_stub},
    260 { stubsize(lwpinfo_stub), lwpinfo_stub},
    261 { stubsize(prusage_stub), prusage_stub},
    262 };
    263 
    264 /*
    265  * read a protocol line, do some checks and extract its key
    266  * and value part.
    267  */
    268 static int
    269 r_line() {
    270 	size_t len;
    271 
    272 	if (fgets(line, P_MAXLEN, rstream) == NULL) {
    273 		format_err("can't read line");
    274 		return (-1);
    275 	}
    276 	len = strlen(line);
    277 	if (len > P_MAXLEN) {
    278 		format_err("%s: \"%s\"", "wrong line length", line);
    279 		return (-1);
    280 	}
    281 	/* carriage return */
    282 	if (len == 1) {
    283 		value[0] = line[0];
    284 		return (0);
    285 	}
    286 	/* see P_MAXKEY and P_MAXVAL for string sizes */
    287 	if (sscanf(line, "%19s %58s", key, value) != 2) {
    288 		format_err("%s: \"%s\"", "wrong line format", line);
    289 		return (-1);
    290 	}
    291 	if (strcmp(key, RDERR) == 0) {
    292 		(void) strcpy(error, line + strlen(RDERR) + 1);
    293 		return (-1);
    294 	}
    295 	return (0);
    296 }
    297 
    298 #define	STRUCT_TO_STR	1
    299 #define	STR_TO_STRUCT	2
    300 
    301 /*
    302  * if STR_TO_STRUCT read a 64 bit value from string buffer, format it and
    303  * write it into the structure.
    304  * if STRUCT_TO_STR read a 64 bit value from structure and write it as
    305  * a string into buffer.
    306  */
    307 static int
    308 format_int64(int set, char *buf, char *strct, int off)
    309 {
    310 	int64_t v;
    311 
    312 	if (set == STR_TO_STRUCT) {
    313 		if (sscanf(buf, "%" SCNd64, &v) != 1) {
    314 			format_err("%s: \"%s\"", "wrong line format", line);
    315 			return (-1);
    316 		}
    317 		*(int64_t *)(void *)(strct + off) = v;
    318 
    319 	} else {
    320 		v = *((int64_t *)(void *)(strct + off));
    321 		(void) sprintf(buf, "%" PRId64, v);
    322 
    323 	}
    324 	return (0);
    325 }
    326 
    327 /*
    328  * if STR_TO_STRUCT read a 32 bit value from string buffer, format it and
    329  * write it into the structure.
    330  * if STRUCT_TO_STR read a 32 bit value from structure and write it as
    331  * a string into buffer.
    332  */
    333 static int
    334 format_int32(int set, char *buf, char *id, int off)
    335 {
    336 	int32_t v;
    337 
    338 	if (set == STR_TO_STRUCT) {
    339 		if (sscanf(buf, "%d", &v) != 1) {
    340 			format_err("%s: \"%s\"", "wrong line format", line);
    341 			return (-1);
    342 		}
    343 		*(int32_t *)(void *)(id + off) = v;
    344 
    345 	} else {
    346 		v = *((int32_t *)(void *)(id + off));
    347 		(void) sprintf(buf, "%d", v);
    348 
    349 	}
    350 	return (0);
    351 }
    352 
    353 /*
    354  */
    355 static int
    356 format_ulong(int set, char *buf, char *id, int off)
    357 {
    358 	ulong_t v;
    359 
    360 	if (set == STR_TO_STRUCT) {
    361 		if (sscanf(buf, "%lu", &v) != 1) {
    362 			format_err("%s: \"%s\"", "wrong line format", line);
    363 			return (-1);
    364 		}
    365 		*(ulong_t *)(void *)(id + off) = v;
    366 
    367 	} else {
    368 		v = *((ulong_t *)(void *)(id + off));
    369 		(void) sprintf(buf, "%ld", v);
    370 
    371 	}
    372 	return (0);
    373 }
    374 
    375 /*
    376  * if STR_TO_STRUCT read a float value from string buffer, format it and
    377  * write it into the structure.
    378  * if STRUCT_TO_STR read a float value from structure and write it as
    379  * a string into buffer.
    380  */
    381 static int
    382 format_float(int set, char *buf, char *id, int off)
    383 {
    384 	float v;
    385 
    386 	if (set == STR_TO_STRUCT) {
    387 		if (sscanf(buf, "%f", &v) != 1) {
    388 			format_err("%s: \"%s\"", "wrong line format", line);
    389 			return (-1);
    390 		}
    391 		*(float *)(void *)(id + off) = v;
    392 
    393 	} else {
    394 		v = *((float *)(void *)(id + off));
    395 		(void) sprintf(buf, "%f", v);
    396 
    397 	}
    398 	return (0);
    399 }
    400 
    401 /*
    402  * if STR_TO_STRUCT read a double value from string buffer, format it and
    403  * write it into the structure.
    404  * if STRUCT_TO_STR read a double value from structure and write it as
    405  * a string into buffer.
    406  */
    407 static int
    408 format_double(int set, char *buf, char *id, int off)
    409 {
    410 	double v;
    411 
    412 	if (set == STR_TO_STRUCT) {
    413 		if (sscanf(buf, "%lf", &v) != 1) {
    414 			format_err("wrong line format: \"%s\"", line);
    415 			return (-1);
    416 		}
    417 		*(double *)(void *)(id + off) = v;
    418 
    419 	} else {
    420 		v = *((double *)(void *)(id + off));
    421 		(void) sprintf(buf, "%f", v);
    422 
    423 	}
    424 	return (0);
    425 }
    426 
    427 /*
    428  * if STR_TO_STRUCT read a string from string buffer, format it and
    429  * write it into the structure.
    430  * if STRUCT_TO_STR read a string from structure and write it as
    431  * a string into buffer.
    432  */
    433 static int
    434 format_string(int set, char *buf, char *id, int off)
    435 {
    436 	char *v;
    437 
    438 	if (set == STR_TO_STRUCT) {
    439 		if ((v = (char *)malloc(strlen(buf) + 1))  != 0) {
    440 			(void) strcpy(v, buf);
    441 		} else {
    442 			v = nullstr;
    443 			return (-1);
    444 		}
    445 		*(char **)(void *)(id + off) = v;
    446 	} else {
    447 		if ((*((char **)(void *)(id + off))) != NULL) {
    448 			(void) snprintf(buf, P_MAXVAL, "%s",
    449 			    *((char **)(void *)(id + off)));
    450 		}
    451 	}
    452 	return (0);
    453 }
    454 
    455 static int
    456 format_timestruc(int set, char *buf, char *strct, int off)
    457 {
    458 	int64_t v1;
    459 	int64_t v2;
    460 
    461 	if (set == STR_TO_STRUCT) {
    462 		if (sscanf(buf, "%" SCNd64 ",%" SCNd64, &v1, &v2) != 2) {
    463 			format_err("%s: \"%s\"", "wrong line format", line);
    464 			return (-1);
    465 		}
    466 		((timestruc_t *)(void *)(strct + off))->tv_sec = v1;
    467 		((timestruc_t *)(void *)(strct + off))->tv_nsec = v2;
    468 
    469 	} else {
    470 		v1 = ((timestruc_t *)(void *)(strct + off))->tv_sec;
    471 		/*
    472 		 * Since the times in prusage start with millisecond
    473 		 * precision after the micro state accounting was enabled
    474 		 * discard the nano/micro second fraction in the saved
    475 		 * values otherwise we will get negative values in next run.
    476 		 */
    477 		v2 = ((((timestruc_t *)(void *)(strct + off))->tv_nsec) /
    478 			MICROSEC) * MICROSEC;
    479 		(void) sprintf(buf, "%" PRId64 ",%" PRId64, v1, v2);
    480 
    481 	}
    482 	return (0);
    483 }
    484 
    485 /*
    486  * A hash table of keys == names and data == { formats and offsets }.
    487  */
    488 static int
    489 init_hashtab() {
    490 	ENTRY item;
    491 	int   i, j, size = 0;
    492 
    493 	for (i = 0; i < NOF_STUBS + 1; i++) {
    494 		size += stubs[i].size;
    495 	}
    496 	if (hcreate(size) == 0) {
    497 		format_err("can't create hash table");
    498 		return (-1);
    499 	}
    500 	for (i = 0; i < NOF_STUBS + 1; i++) {
    501 		for (j = 0; j < stubs[i].size; j++) {
    502 			item.key = stubs[i].stub[j].key;
    503 			item.data = (void *) &(stubs[i].stub[j].info);
    504 			if (hsearch(item, ENTER) == NULL) {
    505 				format_err("can't insert into hash table");
    506 				return (-1);
    507 			}
    508 		}
    509 	}
    510 	return (0);
    511 }
    512 
    513 int
    514 open_prot(int fd, char *rw)
    515 {
    516 	if (strcmp(rw, "r") == 0) {
    517 		if ((rstream = fdopen(fd, rw)) == NULL) {
    518 			format_err("can't open read stream");
    519 			return (-1);
    520 		}
    521 		if (init_hashtab() != 0) {
    522 			format_err("can't initialize hashtab");
    523 			return (-1);
    524 		}
    525 	} else if (strcmp(rw, "w") == 0) {
    526 		if ((wstream = fdopen(fd, rw)) == NULL) {
    527 			format_err("can't open write stream");
    528 			return (-1);
    529 		}
    530 	} else {
    531 		format_err("open_prot(), wrong argument  %s", rw);
    532 			return (-1);
    533 	}
    534 	return (0);
    535 }
    536 
    537 void
    538 close_prot()
    539 {
    540 
    541 	(void) fclose(rstream);
    542 	(void) fclose(wstream);
    543 	hdestroy();
    544 }
    545 
    546 /*
    547  * @RDS-MAG@
    548  * PROTV 100
    549  */
    550 int
    551 wr_phead()
    552 {
    553 	(void) fprintf(wstream, "%s\n%s %d\n",
    554 	    PROTM, PROTV, PROT_VERSION);
    555 	(void) fflush(wstream);
    556 	return (0);
    557 }
    558 /*
    559  * @RDS@> [code]
    560  */
    561 int
    562 wr_prompt(char *code) {
    563 
    564 	(void) fprintf(wstream, "%s%s\n", PROMPT, code);
    565 	(void) fflush(wstream);
    566 	return (0);
    567 }
    568 
    569 int
    570 wr_lshead(int n)
    571 {
    572 	(void) fprintf(wstream, "%s %d\n", LISTN, n);
    573 	(void) fflush(wstream);
    574 	return (0);
    575 }
    576 
    577 /*
    578  * LISTT [type]
    579  * ELEMN [n]
    580  */
    581 int
    582 wr_lhead(int type, int n)
    583 {
    584 	(void) fprintf(wstream, "%s %d\n%s %d\n", LISTT, type, ELEMN, n);
    585 	(void) fflush(wstream);
    586 	return (0);
    587 }
    588 /*
    589  * ELMID [elemid]
    590  * FILDN [number of elements]
    591  * e.g.
    592  * id_usr 11050000000
    593  * id_sys 7850000000
    594  * id_ttime 0
    595  * id_tpftime 0
    596  *
    597  * Write all fields defined by stub[stubidx]. The src is the source pointer.
    598  * For each element read the key, grab the format function and the offset.
    599  * Read and format the element from the source and write it out as a string.
    600  */
    601 int
    602 wr_element(int stubidx, char *src, char *elemid)
    603 {
    604 	int i;
    605 
    606 	(void) fprintf(wstream, "%s %s\n%s %d\n",
    607 	    ELMID, elemid, FILDN, stubs[stubidx].size);
    608 	for (i = 0; i < stubs[stubidx].size; i++) {
    609 		stubs[stubidx].stub[i].info.format(STRUCT_TO_STR,
    610 		    value, src, stubs[stubidx].stub[i].info.off);
    611 		(void) fprintf(wstream, "%s %s\n",
    612 		    stubs[stubidx].stub[i].key, value);
    613 	}
    614 	(void) fflush(wstream);
    615 	return (0);
    616 }
    617 
    618 int
    619 wr_string(char *str)
    620 {
    621 
    622 	(void) fprintf(wstream, "%s", str);
    623 	(void) fflush(wstream);
    624 	return (0);
    625 }
    626 
    627 int
    628 wr_value(char *key, int64_t v)
    629 {
    630 
    631 	(void) fprintf(wstream, "%s %" PRId64 "\n", key, v);
    632 	(void) fflush(wstream);
    633 	return (0);
    634 }
    635 /*
    636  * RDERR [err]
    637  */
    638 void
    639 wr_error(char *err)
    640 {
    641 	size_t len = strlen(RDERR + 1);
    642 	if (strlen(err) > P_MAXLEN - len) {
    643 		*(err + P_MAXLEN - len - 4) = '.';
    644 		*(err + P_MAXLEN - len - 3) = '.';
    645 		*(err + P_MAXLEN - len - 2) = '.';
    646 		*(err + P_MAXLEN - len - 1) = 0;
    647 	}
    648 	len = strlen(err) - 1;
    649 	if (strlen(err) == 0) {
    650 		return;
    651 	}
    652 	while (len-- > 0) {
    653 		if (*(err + len) == '\n')
    654 			*(err + len) = ' ';
    655 	}
    656 
    657 	(void) fprintf(wstream, "%s %s\n", RDERR, err);
    658 	(void) fflush(wstream);
    659 }
    660 
    661 /*
    662  * read a protocol line, check the key and return the value associated
    663  * with it.
    664  */
    665 int64_t
    666 r_value(char *check_key) {
    667 	int64_t v = -1;
    668 
    669 	if ((r_line() == -1) ||
    670 			(strcmp(check_key, key) != 0) ||
    671 			(sscanf(value, "%" SCNd64, &v) != 1)) {
    672 		return (-1);
    673 	}
    674 	return (v);
    675 }
    676 
    677 char *
    678 r_cmd()
    679 {
    680 
    681 	if (r_line() == -1) {
    682 		format_err("can't read command");
    683 		return (NULL);
    684 	}
    685 	return (value);
    686 }
    687 
    688 int
    689 r_phead()
    690 {
    691 	int protv;
    692 	size_t len = strlen(PROTM);
    693 	size_t errorlen = strlen(RDERR);
    694 	int i = 0;
    695 
    696 	while (i++ < MAX_RETRIES) {
    697 		if (fgets(line, P_MAXLEN, rstream) == NULL) {
    698 			format_err("can't read prot. head");
    699 			return (-1);
    700 		}
    701 		len = strlen(line);
    702 		if (len > P_MAXLEN)
    703 			continue;
    704 		if (strcmp(line, PROTM) == 0)
    705 			break;
    706 		if (strncmp(line, RDERR, errorlen) == 0) {
    707 			(void) strcpy(error, line + strlen(RDERR) + 1);
    708 			return (-1);
    709 		}
    710 	}
    711 	if ((protv = r_value(PROTV)) == -1) {
    712 		format_err("can't read prot. version");
    713 		return (-1);
    714 	}
    715 	if (protv != PROT_VERSION) {
    716 		format_err("unsupported prot. version");
    717 		return (-1);
    718 	}
    719 	return (0);
    720 }
    721 
    722 int
    723 r_lshead()
    724 {
    725 	int  ret;
    726 
    727 	if ((ret = r_value(LISTN)) == -1) {
    728 		format_err("can't read number of lists");
    729 		return (-1);
    730 	}
    731 	return (ret);
    732 }
    733 
    734 int
    735 r_lhead(int *type)
    736 {
    737 
    738 	if ((*type = r_value(LISTT)) == -1) {
    739 		format_err("can't read list type");
    740 		return (-1);
    741 	}
    742 	return (r_value(ELEMN));
    743 }
    744 
    745 int
    746 r_element(char *src, char *elemid)
    747 {
    748 	int fn, i;
    749 	ENTRY item, *fitem;
    750 
    751 	if (r_line() == -1) {
    752 		format_err("can't read element id");
    753 		return (-1);
    754 	} else {
    755 		(void) strcpy(elemid, value);
    756 	}
    757 	if ((fn = r_value(FILDN)) == -1) {
    758 		format_err("can't read number of fields");
    759 		return (-1);
    760 	}
    761 	for (i = 0; i < fn; i++) {
    762 		if (r_line() == -1) {
    763 			return (-1);
    764 		} else {
    765 			item.key = key;
    766 			if ((fitem = hsearch(item, FIND)) == NULL) {
    767 				format_err("%s: \"%s\" ",
    768 						"unknown key ", line);
    769 				return (-1);
    770 			}
    771 			((info_t *)(void *)fitem->data)->
    772 				format(STR_TO_STRUCT, value, src,
    773 					((info_t *)(void *)fitem->data)->off);
    774 			}
    775 	}
    776 	return (0);
    777 }
    778 
    779 int
    780 skip_line()
    781 {
    782 	if (r_line() == -1) {
    783 		format_err("can't read element id");
    784 		return (-1);
    785 	} else {
    786 		return (0);
    787 	}
    788 }
    789