/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (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
* 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
*/
/*
*/
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <ctype.h>
#include <errno.h>
#include <libintl.h>
#include <locale.h>
#include <fcntl.h>
#include <libdlpi.h>
#include <libdladm.h>
#include <libdlib.h>
#include <libdllink.h>
/*
* generic entry
* placed at the top of all entry types
*/
typedef struct datadm_entry {
/*
* list structure
* can be manipulated using datadm_walk_list or
* datadm_enqueue_entry
*/
typedef struct datadm_list {
/*
* internal representation of the version string in
* dat.conf or service_provider.conf. the format is
* <dv_name><dv_major>.<dv_minor>
*/
typedef struct datadm_version {
char *dv_name;
/*
* each sp_entry corresponds to an entry in dat.conf or
* service_provider.conf. an sp_entry is processed by the
* function datadm_process_sp_entry.
*/
typedef struct datadm_sp_entry {
char *spe_devname;
int spe_threadsafe;
int spe_default;
char *spe_libpath;
char *spe_sp_data;
int spe_invalid;
/*
* an hca_entry is created whenever a new hca device is
* encountered during sp_entry processing. this structure
* contains two lists. the sp_list holds sp entries that
* are added when sp entry processing occurs. duplicate
* sp entries are not added to this list. the ia_list may
* be built statically using the information in dat.conf or
* dynamically. similar to the sp_list,
* the ia_list contains only unique entries.
*/
typedef struct datadm_hca_entry {
char *he_name;
/*
* an ia_entry is created when a new ia name is encountered
* during sp_entry processing or when a new ia name is
* discovered by datadm_build_ia_lists. ia_entry holds the ia
* device's instance number.
*/
typedef struct datadm_ia_entry {
/*
* a comment entry represents one of the comment lines at the
* top of dat.conf. a list of these lines are saved during the
* when dat.conf gets regenerated.
*/
typedef struct datadm_cmnt_entry {
char *cmnt_line;
typedef struct datadm_hca_find_by_name {
char *hf_name;
/*
* 2nd argument to datadm_hca_entry_find.
* hf_hca_entry is filled in if an hca_entry with
* a matching he_name is found.
*/
typedef struct datadm_hca_find {
/*
* 2nd argument to datadm_ia_entry_find.
* if_ia_entry is filled in if an ia_entry with
* a matching ia_name is found.
*/
typedef struct datadm_ia_find {
char *if_ia_name;
/*
* this gets passed to datadm_add_plink.
*/
typedef struct datadm_fill_ia_list {
int ia_ibnex_fd;
int ia_sock_fd_v4;
int ia_sock_fd_v6;
/*
* this defines the commandline parameters specified
* by the user.
*/
typedef struct datadm_args {
char *da_sp_conf;
char *da_dat_conf;
int da_op_type;
static char *datadm_conf_header_default =
"#\n"
"All rights reserved.\n"
"#\n"
"# DAT configuration file.\n"
"#\n"
"# This file is updated using the datadm(1) command.\n"
"# Do not hand edit this file.\n"
"# See datadm(1) man page for more details.\n"
"#\n"
"# The fields in this file are -\n"
"#\n"
"# IAname version threadsafe default library-path provider-version \\\n"
"# instance-data platform-information\n"
"#\n";
/*
* common parsing functions.
*/
typedef int (*datadm_parse_func_t)(char *, void *);
static int datadm_parse_line(char *, char *[], int *);
static int datadm_parse_generic_str(char *, char **);
static int datadm_parse_nonnull_str(char *, char **);
static int datadm_parse_version(char *, datadm_version_t *);
static int datadm_parse_devname(char *, datadm_sp_entry_t *);
static int datadm_parse_api_version(char *, datadm_sp_entry_t *);
static int datadm_parse_threadsafe(char *, datadm_sp_entry_t *);
static int datadm_parse_default(char *, datadm_sp_entry_t *);
static int datadm_parse_libpath(char *, datadm_sp_entry_t *);
static int datadm_parse_sp_version(char *, datadm_sp_entry_t *);
static int datadm_parse_sp_data(char *, datadm_sp_entry_t *);
static int datadm_parse_ia_name(char *, char *);
/*
* utility functions
*/
static int datadm_walk_list(datadm_list_t *,
int (*)(datadm_entry_t *, void *), void *);
static int datadm_str_match(char *, char *);
/*
* entry allocation/deallocation
*/
static datadm_sp_entry_t *datadm_alloc_sp_entry(void);
static datadm_ia_entry_t *datadm_alloc_ia_entry(void);
static datadm_hca_entry_t *datadm_alloc_hca_entry(void);
static datadm_cmnt_entry_t *datadm_alloc_cmnt_entry(void);
static void datadm_free_sp_entry(datadm_sp_entry_t *);
static void datadm_free_ia_entry(datadm_ia_entry_t *);
static void datadm_free_hca_entry(datadm_hca_entry_t *);
static void datadm_free_cmnt_entry(datadm_cmnt_entry_t *);
/*
* high level parsing functions
*/
static int datadm_parse_sp_conf(datadm_list_t *);
static int datadm_parse_dat_conf(datadm_list_t *);
char *);
/*
* ia devices discovery
*/
static int datadm_build_ia_lists(datadm_list_t *);
/*
* helper function for OP_REMOVE
*/
static void datadm_invalidate_common_sp_entries(datadm_list_t *,
datadm_list_t *);
/*
* output generation
*/
static int datadm_generate_dat_conf(datadm_list_t *);
static int datadm_generate_conf_header(FILE *);
/*
* datadm operations
*/
static int datadm_view(void);
static int datadm_update(void);
static int datadm_add(void);
static int datadm_remove(void);
/*
* usage
*/
static void datadm_usage(void);
/*
* parse function tables
*/
};
};
/*
* operation table
*/
};
static void
datadm_usage(void)
{
"usage: datadm -v\n"
" -u\n"
" -a <service_provider.conf>\n"
" -r <service_provider.conf>\n"));
}
static int
{
int len;
return (-1);
}
return (0);
}
/*
* this function strips off leading and trailing
* whitespaces and returns an error for null or
* empty strings.
*/
static int
{
int len, i;
char *start;
if (str[0] == '\0') {
return (-1);
}
for (i = 0; str[i] != '\0'; i++) {
break;
}
}
for (; str[i] != '\0'; i++) {
str[i] = '\0';
}
}
return (-1);
}
return (0);
}
/*
* parses the api_version and sp_version fields in
*/
static int
{
int i = 0, len;
for (i = 0; i < len; i++) {
}
if (i == len) {
return (-1);
}
if (i > 0) {
} else {
}
major_idx = i;
for (; i < len; i++) {
}
if (i == len) {
return (-1);
}
if (str[i] != '.') {
return (-1);
}
minor_idx = ++i;
if (i == len) {
return (-1);
}
for (; i < len; i++) {
}
if (i != len) {
return (-1);
}
return (0);
}
/*
* parses the ia_name field in dat.conf
*/
static int
{
return (-1);
return (0);
}
/*
* parses the device name, strips leading and trailing spaces.
* the format should be "driver_name=<dev_name>"
*/
static int
{
/*
* strip out leading spaces and try to match
* the expected string
*/
for (i = 0; i < len; i++) {
continue;
} else {
j++;
if (j == dlen) {
break;
} else {
continue;
}
} else {
break;
}
}
}
/*
* j must be dlen if the matching string is found
*/
if (j != dlen) {
return (-1);
}
/*
* skip past the last char of drv_name
*/
i++;
/*
* strip the spaces before the '='
*/
for (; i < len; i++) {
break;
}
}
/*
* return if the string is too long or if
* the '=' isn't found
*/
return (-1);
}
i++;
if (i >= len) {
/*
* no string after the equal
*/
return (-1);
}
}
static int
{
}
static int
{
int retval = 0;
sp_entry->spe_threadsafe = 0;
} else {
retval = -1;
}
return (retval);
}
static int
{
int retval = 0;
sp_entry->spe_default = 0;
} else {
retval = -1;
}
return (retval);
}
static int
{
}
static int
{
}
static int
{
}
static void
{
} else {
}
}
/*
* iterates through the list applying func on each element.
* break and return if func returns non-zero.
*/
static int
void *arg)
{
int retval = 0;
if (retval != 0) break;
}
return (retval);
}
/*
* iterates through the list applying free_func to each element.
* list becomes empty when the function returns.
*/
static void
{
}
}
static datadm_sp_entry_t *
datadm_alloc_sp_entry(void)
{
return (NULL);
}
return (sp_entry);
}
static void
{
}
}
sp_entry->spe_threadsafe = 0;
sp_entry->spe_default = 0;
}
}
}
}
static int
{
return (0);
}
} else {
return (0);
}
}
return (1);
}
static int
{
return (0);
}
return (0);
}
return (0);
}
return (1);
}
static int
{
return (0);
}
&sp2->spe_api_version)) {
return (0);
}
return (0);
}
return (0);
}
return (0);
}
&sp2->spe_sp_version)) {
return (0);
}
return (0);
}
return (1);
}
static datadm_ia_entry_t *
datadm_alloc_ia_entry(void)
{
return (NULL);
}
return (ia_entry);
}
static void
{
}
static datadm_hca_entry_t *
datadm_alloc_hca_entry(void)
{
return (NULL);
}
return (hca_entry);
}
static void
{
}
(void (*)(datadm_entry_t *))datadm_free_sp_entry);
(void (*)(datadm_entry_t *))datadm_free_ia_entry);
}
static int
{
return (0);
}
return (1);
}
static int
{
return (1);
}
return (0);
}
static int
{
return (1);
}
return (0);
}
static datadm_cmnt_entry_t *
datadm_alloc_cmnt_entry(void)
{
if (cmnt_entry == NULL) {
return (NULL);
}
return (cmnt_entry);
}
static void
{
}
}
/*
* tokenizes a line and strips off the quotes from quoted strings
*/
static int
{
int len, i;
int count = 0;
/* the line must not be longer than DATADM_LINESZ */
return (-1);
}
/* discard blank lines and comments */
if (len == 1) {
*token_count = 0;
return (0);
}
*token_count = 0;
return (0);
}
/* removes the new line */
len--;
for (i = 0; i < len; i++) {
/*
* start points to the start of
* a new token. if start is '"',
* we should expect a quoted
* string.
*/
if (*start == '\"') {
/*
* keep scanning until we
* hit the end quote.
*/
if (line_buf[i] != '\"') {
continue;
}
/*
* skip past the start quote
*/
start++;
} else {
/*
* our token is not a quoted
* string. our token ends only
* when we hit a whitespace.
*/
continue;
}
}
/*
* nullify the end quote (if any)
* and update the tokens array.
*/
line_buf[i] = '\0';
count++;
} else {
/*
* skip whitespaces
*/
continue;
} else {
}
}
if (count == DATADM_MAX_TOKENS) {
break;
}
}
count++;
}
*token_count = count;
return (0);
}
/*
* attempts to save sp_entry into hca_list.
* becomes no-op if sp entry already exists.
* new hca entries and ia entries are created as needed.
*/
static int
char *ia_name)
{
datadm_hca_entry_find, (void *)&hca_find);
int dlen;
/*
* hca_entry not found, need to create
* and insert one.
*/
return (-1);
}
return (-1);
}
} else {
}
goto put_sp_entry;
}
/*
* ia_entry not found, need to create
* and insert one.
*/
return (-1);
}
(datadm_entry_t *)ia_entry);
}
(int (*)(datadm_entry_t *, void *))datadm_sp_entry_match,
(void *)sp_entry)) {
return (1);
} else {
/*
* only insert sp_entry if it is not found.
*/
(datadm_entry_t *)sp_entry);
}
return (0);
}
/*
* parses service_provider.conf
*/
static int
{
int retval = 0;
int token_count = 0;
int line_count = 0;
return (-1);
}
for (;;) {
break;
}
token_count = 0;
line_count++;
if (retval != 0) {
"datadm: %s: line %d exceeded max length %d\n"),
break;
}
if (token_count == 0) continue;
if (token_count == DATADM_NUM_SP_TOKENS) {
int i = 0;
retval = -1;
break;
}
/*
* sp_entry gets filled incrementally by
* each parsing function
*/
for (i = 0; i < DATADM_NUM_SP_TOKENS &&
retval == 0; i++) {
retval = (*datadm_sp_parse_funcs[i])
}
if (retval != 0) {
"datadm: parse error: %s, "
"line %d, token: %s\n"),
break;
}
if (retval != 0) {
if (retval == 1) {
retval = 0;
} else {
break;
}
}
} else {
"datadm: parse error: %s, line %d, "
"# of tokens: %d, expected %d\n"), sp_conf,
retval = -1;
break;
}
}
if (retval != 0) {
(void (*)(datadm_entry_t *))datadm_free_hca_entry);
}
return (retval);
}
/*
* parses dat.conf
*/
static int
{
int retval = 0;
int token_count = 0;
int line_count = 0;
/* dat.conf not existing is not an error for OP_ADD */
return (0);
}
dat_conf);
return (-1);
}
for (;;) {
break;
}
token_count = 0;
line_count++;
if (retval != 0) {
"datadm: %s: line %d exceeded max length %d\n"),
break;
}
if (token_count == 0) {
int cmnt_len;
/*
* comments are saved only if they are
* at the top of dat.conf.
*/
if (!save_header) continue;
if (cmnt_entry == NULL) {
perror("datadm: malloc");
retval = -1;
break;
}
perror("datadm: malloc");
retval = -1;
break;
}
(datadm_entry_t *)cmnt_entry);
continue;
}
if (token_count == DATADM_NUM_DAT_TOKENS) {
int i = 0;
/*
* we stop saving comment lines once
* we see the first valid line.
*/
retval = -1;
break;
}
/*
* sp_entry gets filled incrementally by
* each parsing function
*/
for (i = 0; i < DATADM_NUM_DAT_TOKENS &&
retval == 0; i++) {
void *arg;
if (i == 0) {
/*
* the first token (ia name)
* does not belong to an
* sp_entry
*/
} else {
}
retval = (*datadm_dat_parse_funcs[i])
}
if (retval != 0) {
"datadm: parse error: %s, "
"line %d, token: %s\n"), dat_conf,
break;
}
/*
* we ignore the ibds in dat.conf if we are
* doing update
*/
} else {
}
if (retval != 0) {
if (retval == 1) {
retval = 0;
} else {
break;
}
}
} else {
"datadm: parse error: %s, line %d, "
"# of tokens: %d, expected %d\n"), dat_conf,
retval = -1;
break;
}
}
if (retval != 0) {
(void (*)(datadm_entry_t *))datadm_free_cmnt_entry);
(void (*)(datadm_entry_t *))datadm_free_hca_entry);
}
return (retval);
}
/*
* used by OP_REMOVE to invalidate common sp entries between hl1 and hl2.
* invalid sp entries will be ignored by datadm_generate_dat_conf.
*/
static void
{
if (!datadm_hca_entry_match(
(datadm_hca_entry_t *)he2)) {
continue;
}
if (!datadm_sp_entry_match(
(datadm_sp_entry_t *)se1,
(datadm_sp_entry_t *)se2)) {
continue;
}
((datadm_sp_entry_t *)se1)->
spe_invalid = 1;
break;
}
}
break;
}
}
}
static int
{
return (1);
}
return (0);
}
{
(void) datadm_walk_list(hca_list,
(int (*)(datadm_entry_t *, void *))datadm_hca_entry_find_by_name,
&hf);
return (hf.hf_hca_entry);
}
static boolean_t
{
NULL) != DLADM_STATUS_OK) ||
(class != DATALINK_CLASS_PART) ||
DLADM_OPT_ACTIVE) != DLADM_STATUS_OK)) {
return (B_FALSE);
}
/*
* we don't really need to know the ip address.
* we just want to check if the device is plumbed
* or not.
*/
/*
* we try v6 if the v4 address isn't found.
*/
return (B_FALSE);
}
return (B_FALSE);
return (B_FALSE);
(int (*)(datadm_entry_t *, void *))
/*
* we insert an ia entry only if
* it is unique.
*/
(datadm_entry_t *)ia_entry);
}
}
return (B_FALSE);
}
/*
* build ia lists for each hca_list element
*/
static int
{
return (-1);
goto out;
perror("datadm: socket");
goto out;
}
perror("datadm: socket");
goto out;
}
&ia_args, 0);
rv = 0;
out:
if (sv4 != -1)
if (sv6 != -1)
if (fd != -1)
return (rv);
}
static int
{
int retval;
"%s %s%d.%d %s %s %s %s%d.%d \"%s\" \"%s%s%s\"\n",
if (retval < 0) {
return (-1);
}
return (0);
}
/*
* generate dat.conf header
*/
static int
{
int retval = 0;
/*
* if dat.conf doesn't have a header, we prepend a
* default one.
*/
goto done;
}
int len;
if (retval < 0) {
break;
}
/*
* append a newline if the comment line doesn't
* have one.
*/
if (retval < 0) {
break;
}
}
}
}
done:;
if (retval < 0) {
return (-1);
}
return (0);
}
/*
*/
static int
{
int retval = 0;
} else {
"datadm: cannot open %s: %s\n"),
return (-1);
}
}
/*
* do not generate the header if we are
* printing to the screen
*/
if (retval != 0) {
goto done;
}
}
continue;
}
(datadm_ia_entry_t *)iep,
(datadm_sp_entry_t *)sep);
if (retval != 0) {
goto done;
}
}
}
}
done:;
}
if (retval < 0) {
perror("datadm: fprintf");
}
return (retval);
}
static int
datadm_view(void)
{
int retval = 0;
if (retval != 0) {
goto cleanup;
}
if (retval != 0) {
goto cleanup;
}
cleanup:;
(void (*)(datadm_entry_t *))datadm_free_cmnt_entry);
(void (*)(datadm_entry_t *))datadm_free_hca_entry);
return (retval);
}
static int
datadm_update(void)
{
int retval = 0;
if (retval != 0) {
goto cleanup;
}
if (retval != 0) {
goto cleanup;
}
if (retval != 0) {
goto cleanup;
}
cleanup:;
(void (*)(datadm_entry_t *))datadm_free_cmnt_entry);
(void (*)(datadm_entry_t *))datadm_free_hca_entry);
return (retval);
}
static int
datadm_add(void)
{
int retval = 0;
if (retval != 0) {
goto cleanup;
}
if (retval != 0) {
goto cleanup;
}
if (retval != 0) {
goto cleanup;
}
if (retval != 0) {
goto cleanup;
}
cleanup:;
(void (*)(datadm_entry_t *))datadm_free_cmnt_entry);
(void (*)(datadm_entry_t *))datadm_free_hca_entry);
return (retval);
}
static int
datadm_remove(void)
{
int retval = 0;
if (retval != 0) {
goto cleanup;
}
if (retval != 0) {
goto cleanup;
}
if (retval != 0) {
goto cleanup;
}
cleanup:;
(void (*)(datadm_entry_t *))datadm_free_cmnt_entry);
(void (*)(datadm_entry_t *))datadm_free_hca_entry);
(void (*)(datadm_entry_t *))datadm_free_hca_entry);
return (retval);
}
static int
{
char *dat_conf;
return (0);
}
return (-1);
}
dat_conf[0] = '\0';
return (0);
}
int
{
extern char *optarg;
extern int optind;
int c, retval;
#if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
#endif
(void) textdomain(TEXT_DOMAIN);
switch (c) {
case 'v':
break;
case 'u':
break;
case 'a':
break;
case 'r':
break;
case 'b':
break;
default:
errflg = 1;
break;
}
if (errflg != 0) {
break;
}
}
datadm_usage();
return (1);
}
if (datadm_locate_dat_conf(basedir)) {
return (1);
}
return (retval);
}