rdprot.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <search.h>
#include <stdlib.h>
#include <sys/utsname.h>
#include "rdprot.h"
#include "rdutil.h"
/*
* This file works out the protocol layer of the bidirectional data interface
* between the rds and the client. In the server mode rds writes greetings and
* a protocol header to the output stream.
* pheader == { "@RDS-MAG@" PROTV }
* PROTV == { protocol version }
* Then it sends a prompt and waits for command from client.
* PROMPT == { "@RDS@>" }
* COMMAND == { "command" cmd }
* cmd == { "-pUuJjS" | "-p" | "-u" | "-U" |
* "-j" | "-J" | "-S" | "-i100" | "alive"| "exit" }
* The answer from rds is always a lists of data. The header of the answer data
* contains the number of lists in the package. Each list has a header and
* some elements, which have again a header and some fields of data:
* answer == { lshead, n * list }
* lshead == { number of lists }
* list == { lheader, n * element }
* lheader == { LISTT, ELEMN }
* LISTT == { type of the list }
* ELEMN == { number of elements in the list }
* element == { eheader, field }
* eheader == { ELMID, FILDN }
* ELMID == { element id, like pid, uid, project name }
* field == { KEY, VALUE }
* All protocol elements have a key and a value separated by one space.
* The value begins after the first space and ends with the new line character.
* Protocol keys are: "@RDS-MAG@", PROTV, LISTN, LISTT, ELEMN ELMID, FILDN,
* RDERR. The special key RDERR can occur in any line and indicates that an
* error condition occurred, where the VALUE is the error message.
*/
static char line[P_MAXLEN];
static char error[P_MAXLEN];
static char value[P_MAXVAL];
static char key[P_MAXKEY];
static char *nullstr = "";
static FILE *wstream, *rstream;
static int format_int64(int, char *, char *, int);
static int format_int32(int, char *, char *, int);
static int format_ulong(int, char *, char *, int);
static int format_float(int, char *, char *, int);
static int format_double(int, char *, char *, int);
static int format_string(int, char *, char *, int);
static int format_timestruc(int, char *, char *, int);
/*
* The kv_pair_t represents an field in a c-sturcture. An filed
* is defined by a key 'field name', format function and an offset
* in this structure
*/
/*
* Array of fields from id_info_t structure, that are sent/received
* in a process/user/project utilization list.
*/
static kv_pair_t id_stub[] =
{
{ "id_pid", { format_int32, offsetof(id_info_t, id_pid) }},
{ "id_uid", { format_int32, offsetof(id_info_t, id_uid) }},
{ "id_projid", { format_int32, offsetof(id_info_t, id_projid) }},
{ "id_usr", { format_double, offsetof(id_info_t, id_usr) }},
{ "id_sys", { format_double, offsetof(id_info_t, id_sys) }},
{ "id_ttime", { format_double, offsetof(id_info_t, id_ttime) }},
{ "id_tpftime", { format_double, offsetof(id_info_t, id_tpftime) }},
{ "id_dpftime", { format_double, offsetof(id_info_t, id_dpftime) }},
{ "id_kpftime", { format_double, offsetof(id_info_t, id_kpftime) }},
{ "id_lck", { format_double, offsetof(id_info_t, id_lck) }},
{ "id_slp", { format_double, offsetof(id_info_t, id_slp) }},
{ "id_lat", { format_double, offsetof(id_info_t, id_lat) }},
{ "id_stime", { format_double, offsetof(id_info_t, id_stime) }},
{ "id_minf", { format_int64, offsetof(id_info_t, id_minf) }},
{ "id_majf", { format_int64, offsetof(id_info_t, id_majf) }},
{ "id_nswap", { format_int64, offsetof(id_info_t, id_nswap) }},
{ "id_inblk", { format_int64, offsetof(id_info_t, id_inblk) }},
{ "id_oublk", { format_int64, offsetof(id_info_t, id_oublk) }},
{ "id_msnd", { format_int64, offsetof(id_info_t, id_msnd) }},
{ "id_mrcv", { format_int64, offsetof(id_info_t, id_mrcv) }},
{ "id_sigs", { format_int64, offsetof(id_info_t, id_sigs) }},
{ "id_vctx", { format_int64, offsetof(id_info_t, id_vctx) }},
{ "id_ictx", { format_int64, offsetof(id_info_t, id_ictx) }},
{ "id_scl", { format_int64, offsetof(id_info_t, id_scl) }},
{ "id_ioch", { format_int64, offsetof(id_info_t, id_ioch) }},
{ "id_hpsize", { format_int64, offsetof(id_info_t, id_hpsize) }},
{ "id_size", { format_int64, offsetof(id_info_t, id_size) }},
{ "id_rssize", { format_int64, offsetof(id_info_t, id_rssize) }},
{ "id_pctcpu", { format_float, offsetof(id_info_t, id_pctcpu) }},
{ "id_pctmem", { format_float, offsetof(id_info_t, id_pctmem) }},
{ "id_time", { format_int64, offsetof(id_info_t, id_time) }},
{ "id_nlwps", { format_int32, offsetof(id_info_t, id_nlwps) }},
{ "id_timestamp", { format_int64, offsetof(id_info_t, id_timestamp) }},
{ "id_nproc", { format_int32, offsetof(id_info_t, id_nproc) }},
{ "id_inpkg", { format_int64, offsetof(id_info_t, id_inpkg) }},
{ "id_oupkg", { format_int64, offsetof(id_info_t, id_oupkg) }},
{ "id_name", { format_string, offsetof(id_info_t, id_name) }}
};
static kv_pair_t lwp_stub[] =
{
{"li_usage", { format_ulong, offsetof(lwp_info_t, li_usr) }},
{"li_usr", { format_ulong, offsetof(lwp_info_t, li_usr) }},
{"li_sys", { format_ulong, offsetof(lwp_info_t, li_sys) }},
{"li_ttime", { format_ulong, offsetof(lwp_info_t, li_ttime) }},
{"li_tpftime", { format_ulong, offsetof(lwp_info_t, li_tpftime) }},
{"li_dpftime", { format_ulong, offsetof(lwp_info_t, li_dpftime) }},
{"li_kpftime", { format_ulong, offsetof(lwp_info_t, li_kpftime) }},
{"li_lck", { format_ulong, offsetof(lwp_info_t, li_lck) }},
{"li_slp", { format_ulong, offsetof(lwp_info_t, li_slp) }},
{"li_lat", { format_ulong, offsetof(lwp_info_t, li_lat) }},
{"li_stime", { format_ulong, offsetof(lwp_info_t, li_stime) }},
{"li_minf", { format_ulong, offsetof(lwp_info_t, li_minf) }},
{"li_majf", { format_ulong, offsetof(lwp_info_t, li_majf) }},
{"li_nswap", { format_ulong, offsetof(lwp_info_t, li_nswap) }},
{"li_inblk", { format_ulong, offsetof(lwp_info_t, li_inblk) }},
{"li_oublk", { format_ulong, offsetof(lwp_info_t, li_oublk) }},
{"li_msnd", { format_ulong, offsetof(lwp_info_t, li_msnd) }},
{"li_mrcv", { format_ulong, offsetof(lwp_info_t, li_mrcv) }},
{"li_sigs", { format_ulong, offsetof(lwp_info_t, li_sigs) }},
{"li_vctx", { format_ulong, offsetof(lwp_info_t, li_vctx) }},
{"li_ictx", { format_ulong, offsetof(lwp_info_t, li_ictx) }},
{"li_scl", { format_ulong, offsetof(lwp_info_t, li_scl) }},
{"li_ioch", { format_ulong, offsetof(lwp_info_t, li_ioch) }},
{"li_hpsize", { format_ulong, offsetof(lwp_info_t, li_hpsize) }},
{"li_timestamp", { format_ulong, offsetof(lwp_info_t, li_timestamp) }},
};
static kv_pair_t lwpinfo_stub[] =
{
{"lwpr_pid", { format_int32, offsetof(lwpinfo_t, pr_pid) }},
{"lwpr_lwpid", { format_int32, offsetof(lwpinfo_t, pr_lwpid) }},
};
static kv_pair_t prusage_stub[] =
{
{"pr_tstamp", { format_timestruc, offsetof(prusage_t, pr_tstamp) }},
{"pr_create", { format_timestruc, offsetof(prusage_t, pr_create) }},
{"pr_term", { format_timestruc, offsetof(prusage_t, pr_term) }},
{"pr_rtime", { format_timestruc, offsetof(prusage_t, pr_rtime) }},
{"pr_utime", { format_timestruc, offsetof(prusage_t, pr_utime) }},
{"pr_stime", { format_timestruc, offsetof(prusage_t, pr_stime) }},
{"pr_ttime", { format_timestruc, offsetof(prusage_t, pr_ttime) }},
{"pr_tftime", { format_timestruc, offsetof(prusage_t, pr_tftime) }},
{"pr_dftime", { format_timestruc, offsetof(prusage_t, pr_dftime) }},
{"pr_kftime", { format_timestruc, offsetof(prusage_t, pr_kftime) }},
{"pr_ltime", { format_timestruc, offsetof(prusage_t, pr_ltime) }},
{"pr_slptime", { format_timestruc, offsetof(prusage_t, pr_slptime) }},
{"pr_wtime", { format_timestruc, offsetof(prusage_t, pr_wtime) }},
{"pr_stoptime", { format_timestruc, offsetof(prusage_t, pr_stoptime) }},
{"pr_minf", { format_ulong, offsetof(prusage_t, pr_minf) }},
{"pr_majf", { format_ulong, offsetof(prusage_t, pr_majf) }},
{"pr_nswap", { format_ulong, offsetof(prusage_t, pr_nswap) }},
{"pr_inblk", { format_ulong, offsetof(prusage_t, pr_inblk) }},
{"pr_oublk", { format_ulong, offsetof(prusage_t, pr_oublk) }},
{"pr_msnd", { format_ulong, offsetof(prusage_t, pr_msnd) }},
{"pr_mrcv", { format_ulong, offsetof(prusage_t, pr_mrcv) }},
{"pr_sigs", { format_ulong, offsetof(prusage_t, pr_sigs) }},
{"pr_vctx", { format_ulong, offsetof(prusage_t, pr_vctx) }},
{"pr_ictx", { format_ulong, offsetof(prusage_t, pr_ictx) }},
{"pr_sysc", { format_ulong, offsetof(prusage_t, pr_sysc) }},
{"pr_ioch", { format_ulong, offsetof(prusage_t, pr_ioch) }},
};
/*
* Array of fields in id_info_t structure, that are sent/received
* in an active user list.
*/
static kv_pair_t usr_stub[] =
{
{ "usr_id", { format_int32, offsetof(id_info_t, id_uid) }},
{ "usr_name", { format_string, offsetof(id_info_t, id_name) }}
};
/*
* Array of fields in id_info_t structure, that are sent/received
* in an active project list.
*/
static kv_pair_t prj_stub[] =
{
{ "prj_id", { format_int32, offsetof(id_info_t, id_projid) }},
{ "prj_name", { format_string, offsetof(id_info_t, id_name) }}
};
/*
* Array of fields in id_info_t structure, that are sent/received
* in a system list.
*/
static kv_pair_t sys_stub[] =
{
{ "sys_nodename", { format_string, offsetof(sys_info_t, nodename) }},
{ "sys_name", { format_string, offsetof(sys_info_t, name) }}
};
/*
* Array of fields in id_info_t structure, that are sent/received
* in command.
*/
static kv_pair_t cmd_stub[] =
{
{ "command", { format_int32, offsetof(cmd_t, command) }}
};
#define stubsize(stub) ((sizeof (stub))/(sizeof (kv_pair_t)))
/*
* Each list type has its own fields description, the list type is
* the index into this table:
* L_PRC_SI - processes statistical information
* L_USR_SI - useres statistical information
* L_PRJ_SI - projects statistical information
* L_AC_USR - active users
* L_AC_PRJ - active projects
* L_SYSTEM - system
*/
#define NOF_STUBS 10
static stub_t stubs[NOF_STUBS + 1] = {
{ 0, NULL},
{ stubsize(id_stub), id_stub},
{ stubsize(id_stub), id_stub},
{ stubsize(id_stub), id_stub},
{ stubsize(usr_stub), usr_stub},
{ stubsize(prj_stub), prj_stub},
{ stubsize(sys_stub), sys_stub},
{ stubsize(cmd_stub), cmd_stub},
{ stubsize(lwp_stub), lwp_stub},
{ stubsize(lwpinfo_stub), lwpinfo_stub},
{ stubsize(prusage_stub), prusage_stub},
};
/*
* read a protocol line, do some checks and extract its key
* and value part.
*/
static int
r_line() {
size_t len;
if (fgets(line, P_MAXLEN, rstream) == NULL) {
format_err("can't read line");
return (-1);
}
len = strlen(line);
if (len > P_MAXLEN) {
format_err("%s: \"%s\"", "wrong line length", line);
return (-1);
}
/* carriage return */
if (len == 1) {
value[0] = line[0];
return (0);
}
/* see P_MAXKEY and P_MAXVAL for string sizes */
if (sscanf(line, "%19s %58s", key, value) != 2) {
format_err("%s: \"%s\"", "wrong line format", line);
return (-1);
}
if (strcmp(key, RDERR) == 0) {
(void) strcpy(error, line + strlen(RDERR) + 1);
return (-1);
}
return (0);
}
#define STRUCT_TO_STR 1
#define STR_TO_STRUCT 2
/*
* if STR_TO_STRUCT read a 64 bit value from string buffer, format it and
* write it into the structure.
* if STRUCT_TO_STR read a 64 bit value from structure and write it as
* a string into buffer.
*/
static int
format_int64(int set, char *buf, char *strct, int off)
{
int64_t v;
if (set == STR_TO_STRUCT) {
if (sscanf(buf, "%" SCNd64, &v) != 1) {
format_err("%s: \"%s\"", "wrong line format", line);
return (-1);
}
*(int64_t *)(void *)(strct + off) = v;
} else {
v = *((int64_t *)(void *)(strct + off));
(void) sprintf(buf, "%" PRId64, v);
}
return (0);
}
/*
* if STR_TO_STRUCT read a 32 bit value from string buffer, format it and
* write it into the structure.
* if STRUCT_TO_STR read a 32 bit value from structure and write it as
* a string into buffer.
*/
static int
format_int32(int set, char *buf, char *id, int off)
{
int32_t v;
if (set == STR_TO_STRUCT) {
if (sscanf(buf, "%d", &v) != 1) {
format_err("%s: \"%s\"", "wrong line format", line);
return (-1);
}
*(int32_t *)(void *)(id + off) = v;
} else {
v = *((int32_t *)(void *)(id + off));
(void) sprintf(buf, "%d", v);
}
return (0);
}
/*
*/
static int
format_ulong(int set, char *buf, char *id, int off)
{
ulong_t v;
if (set == STR_TO_STRUCT) {
if (sscanf(buf, "%lu", &v) != 1) {
format_err("%s: \"%s\"", "wrong line format", line);
return (-1);
}
*(ulong_t *)(void *)(id + off) = v;
} else {
v = *((ulong_t *)(void *)(id + off));
(void) sprintf(buf, "%ld", v);
}
return (0);
}
/*
* if STR_TO_STRUCT read a float value from string buffer, format it and
* write it into the structure.
* if STRUCT_TO_STR read a float value from structure and write it as
* a string into buffer.
*/
static int
format_float(int set, char *buf, char *id, int off)
{
float v;
if (set == STR_TO_STRUCT) {
if (sscanf(buf, "%f", &v) != 1) {
format_err("%s: \"%s\"", "wrong line format", line);
return (-1);
}
*(float *)(void *)(id + off) = v;
} else {
v = *((float *)(void *)(id + off));
(void) sprintf(buf, "%f", v);
}
return (0);
}
/*
* if STR_TO_STRUCT read a double value from string buffer, format it and
* write it into the structure.
* if STRUCT_TO_STR read a double value from structure and write it as
* a string into buffer.
*/
static int
format_double(int set, char *buf, char *id, int off)
{
double v;
if (set == STR_TO_STRUCT) {
if (sscanf(buf, "%lf", &v) != 1) {
format_err("wrong line format: \"%s\"", line);
return (-1);
}
*(double *)(void *)(id + off) = v;
} else {
v = *((double *)(void *)(id + off));
(void) sprintf(buf, "%f", v);
}
return (0);
}
/*
* if STR_TO_STRUCT read a string from string buffer, format it and
* write it into the structure.
* if STRUCT_TO_STR read a string from structure and write it as
* a string into buffer.
*/
static int
format_string(int set, char *buf, char *id, int off)
{
char *v;
if (set == STR_TO_STRUCT) {
if ((v = (char *)malloc(strlen(buf) + 1)) != 0) {
(void) strcpy(v, buf);
} else {
v = nullstr;
return (-1);
}
*(char **)(void *)(id + off) = v;
} else {
if ((*((char **)(void *)(id + off))) != NULL) {
(void) snprintf(buf, P_MAXVAL, "%s",
*((char **)(void *)(id + off)));
}
}
return (0);
}
static int
format_timestruc(int set, char *buf, char *strct, int off)
{
int64_t v1;
int64_t v2;
if (set == STR_TO_STRUCT) {
if (sscanf(buf, "%" SCNd64 ",%" SCNd64, &v1, &v2) != 2) {
format_err("%s: \"%s\"", "wrong line format", line);
return (-1);
}
((timestruc_t *)(void *)(strct + off))->tv_sec = v1;
((timestruc_t *)(void *)(strct + off))->tv_nsec = v2;
} else {
v1 = ((timestruc_t *)(void *)(strct + off))->tv_sec;
/*
* Since the times in prusage start with millisecond
* precision after the micro state accounting was enabled
* discard the nano/micro second fraction in the saved
* values otherwise we will get negative values in next run.
*/
v2 = ((((timestruc_t *)(void *)(strct + off))->tv_nsec) /
MICROSEC) * MICROSEC;
(void) sprintf(buf, "%" PRId64 ",%" PRId64, v1, v2);
}
return (0);
}
/*
* A hash table of keys == names and data == { formats and offsets }.
*/
static int
init_hashtab() {
ENTRY item;
int i, j, size = 0;
for (i = 0; i < NOF_STUBS + 1; i++) {
size += stubs[i].size;
}
if (hcreate(size) == 0) {
format_err("can't create hash table");
return (-1);
}
for (i = 0; i < NOF_STUBS + 1; i++) {
for (j = 0; j < stubs[i].size; j++) {
item.key = stubs[i].stub[j].key;
item.data = (void *) &(stubs[i].stub[j].info);
if (hsearch(item, ENTER) == NULL) {
format_err("can't insert into hash table");
return (-1);
}
}
}
return (0);
}
int
open_prot(int fd, char *rw)
{
if (strcmp(rw, "r") == 0) {
if ((rstream = fdopen(fd, rw)) == NULL) {
format_err("can't open read stream");
return (-1);
}
if (init_hashtab() != 0) {
format_err("can't initialize hashtab");
return (-1);
}
} else if (strcmp(rw, "w") == 0) {
if ((wstream = fdopen(fd, rw)) == NULL) {
format_err("can't open write stream");
return (-1);
}
} else {
format_err("open_prot(), wrong argument %s", rw);
return (-1);
}
return (0);
}
void
close_prot()
{
(void) fclose(rstream);
(void) fclose(wstream);
hdestroy();
}
/*
* @RDS-MAG@
* PROTV 100
*/
int
wr_phead()
{
(void) fprintf(wstream, "%s\n%s %d\n",
PROTM, PROTV, PROT_VERSION);
(void) fflush(wstream);
return (0);
}
/*
* @RDS@> [code]
*/
int
wr_prompt(char *code) {
(void) fprintf(wstream, "%s%s\n", PROMPT, code);
(void) fflush(wstream);
return (0);
}
int
wr_lshead(int n)
{
(void) fprintf(wstream, "%s %d\n", LISTN, n);
(void) fflush(wstream);
return (0);
}
/*
* LISTT [type]
* ELEMN [n]
*/
int
wr_lhead(int type, int n)
{
(void) fprintf(wstream, "%s %d\n%s %d\n", LISTT, type, ELEMN, n);
(void) fflush(wstream);
return (0);
}
/*
* ELMID [elemid]
* FILDN [number of elements]
* e.g.
* id_usr 11050000000
* id_sys 7850000000
* id_ttime 0
* id_tpftime 0
*
* Write all fields defined by stub[stubidx]. The src is the source pointer.
* For each element read the key, grab the format function and the offset.
* Read and format the element from the source and write it out as a string.
*/
int
wr_element(int stubidx, char *src, char *elemid)
{
int i;
(void) fprintf(wstream, "%s %s\n%s %d\n",
ELMID, elemid, FILDN, stubs[stubidx].size);
for (i = 0; i < stubs[stubidx].size; i++) {
stubs[stubidx].stub[i].info.format(STRUCT_TO_STR,
value, src, stubs[stubidx].stub[i].info.off);
(void) fprintf(wstream, "%s %s\n",
stubs[stubidx].stub[i].key, value);
}
(void) fflush(wstream);
return (0);
}
int
wr_string(char *str)
{
(void) fprintf(wstream, "%s", str);
(void) fflush(wstream);
return (0);
}
int
wr_value(char *key, int64_t v)
{
(void) fprintf(wstream, "%s %" PRId64 "\n", key, v);
(void) fflush(wstream);
return (0);
}
/*
* RDERR [err]
*/
void
wr_error(char *err)
{
size_t len = strlen(RDERR + 1);
if (strlen(err) > P_MAXLEN - len) {
*(err + P_MAXLEN - len - 4) = '.';
*(err + P_MAXLEN - len - 3) = '.';
*(err + P_MAXLEN - len - 2) = '.';
*(err + P_MAXLEN - len - 1) = 0;
}
len = strlen(err) - 1;
while (len-- > 0) {
if (*(err + len) == '\n')
*(err + len) = ' ';
}
(void) fprintf(wstream, "%s %s\n", RDERR, err);
(void) fflush(wstream);
}
/*
* read a protocol line, check the key and return the value associated
* with it.
*/
int64_t
r_value(char *check_key) {
int64_t v = -1;
if ((r_line() == -1) ||
(strcmp(check_key, key) != 0) ||
(sscanf(value, "%" SCNd64, &v) != 1)) {
return (-1);
}
return (v);
}
char *
r_cmd()
{
if (r_line() == -1) {
format_err("can't read command");
return (NULL);
}
return (value);
}
int
r_phead()
{
int protv;
size_t len = strlen(PROTM);
size_t errorlen = strlen(RDERR);
int i = 0;
while (i++ < MAX_RETRIES) {
if (fgets(line, P_MAXLEN, rstream) == NULL) {
format_err("can't read prot. head");
return (-1);
}
len = strlen(line);
if (len > P_MAXLEN)
continue;
if (strcmp(line, PROTM) == 0)
break;
if (strncmp(line, RDERR, errorlen) == 0) {
(void) strcpy(error, line + strlen(RDERR) + 1);
return (-1);
}
}
if ((protv = r_value(PROTV)) == -1) {
format_err("can't read prot. version");
return (-1);
}
if (protv != PROT_VERSION) {
format_err("unsupported prot. version");
return (-1);
}
return (0);
}
int
r_lshead()
{
int ret;
if ((ret = r_value(LISTN)) == -1) {
format_err("can't read number of lists");
return (-1);
}
return (ret);
}
int
r_lhead(int *type)
{
if ((*type = r_value(LISTT)) == -1) {
format_err("can't read list type");
return (-1);
}
return (r_value(ELEMN));
}
int
r_element(char *src, char *elemid)
{
int fn, i;
ENTRY item, *fitem;
if (r_line() == -1) {
format_err("can't read element id");
return (-1);
} else {
(void) strcpy(elemid, value);
}
if ((fn = r_value(FILDN)) == -1) {
format_err("can't read number of fields");
return (-1);
}
for (i = 0; i < fn; i++) {
if (r_line() == -1) {
return (-1);
} else {
item.key = key;
if ((fitem = hsearch(item, FIND)) == NULL) {
format_err("%s: \"%s\" ",
"unknown key ", line);
return (-1);
}
((info_t *)(void *)fitem->data)->
format(STR_TO_STRUCT, value, src,
((info_t *)(void *)fitem->data)->off);
}
}
return (0);
}
int
skip_line()
{
if (r_line() == -1) {
format_err("can't read element id");
return (-1);
} else {
return (0);
}
}