dlmgmt_db.c revision b9e076dcc05b713d74073c0d692dfbb0f6f2c594
/*
* 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
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <syslog.h>
#include <pthread.h>
#include <unistd.h>
#include "dlmgmt_impl.h"
typedef enum dlmgmt_db_op {
typedef struct dlmgmt_db_req_s {
struct dlmgmt_db_req_s *ls_next;
/* DLMGMT_PERSIST, not both. */
/*
* List of pending db updates (e.g., because of a read-only filesystem).
*/
uint32_t);
static int dlmgmt_process_db_req(dlmgmt_db_req_t *);
static void *dlmgmt_db_update_thread(void *);
#define MAXLINELEN 1024
/*
* Translator functions to go from dladm_datatype_t to character strings.
* Each function takes a pointer to a buffer, the size of the buffer,
* the name of the attribute, and the value to be written. The functions
* return the number of bytes written to the buffer. If the buffer is not big
* enough to hold the string representing the value, then nothing is written
* and 0 is returned.
*/
/*
* Translator functions to read from a NULL terminated string buffer into
* something of the given DLADM_TYPE_*. The functions each return the number
* of bytes read from the string buffer. If there is an error reading data
* from the buffer, then 0 is returned. It is the caller's responsibility
* to free the data allocated by these functions.
*/
typedef size_t read_func_t(char *, void **);
typedef struct translator_s {
const char *type_name;
} translator_t;
/*
* Translator functions, defined later but declared here so that
* the translator table can be defined.
*/
/*
* Translator table, indexed by dladm_datatype_t.
*/
static translator_t translators[] = {
};
#define LINK_PROPERTY_DELIMINATOR ";"
#define LINK_PROPERTY_TYPE_VALUE_SEP ","
strlen((n)))
/*
* Name of the cache file to keep the active <link name, linkid> mapping
*/
static char cachefile[MAXPATHLEN];
#define DLMGMT_PERSISTENT_DB_PATH "/etc/dladm/datalink.conf"
static size_t
{
/*
* Strings are assumed to be NULL terminated. In order to fit in
* the buffer, the string's length must be less then buffer_length.
* If the value is empty, there's no point in writing it, in fact,
* we shouldn't even see that case.
*/
buffer_length || data_length == 0)
return (0);
/*
* Since we know the string will fit in the buffer, snprintf will
* always return less than buffer_length, so we can just return
* whatever snprintf returns.
*/
}
static size_t
{
/*
* Booleans are either zero or one, so we only need room for two
* characters in the buffer.
*/
return (0);
}
static size_t
{
/*
* Limit checking for uint64_t is a little trickier.
*/
return (0);
}
static size_t
{
>= MAXLINKATTRLEN) {
return (0);
}
/* Account for NULL terminator */
return (len + 1);
}
static size_t
{
return (0);
return (sizeof (boolean_t));
}
static size_t
{
return (0);
return (sizeof (int64_t));
}
static int
{
int err;
/*
* It is either a persistent request or an active request, not both.
*/
return (ENOMEM);
/*
* If the return error is EINPROGRESS, this request is handled
* asynchronously; return success.
*/
if (err != EINPROGRESS)
else
err = 0;
return (err);
}
#define DLMGMT_DB_OP_STR(op) \
#define DLMGMT_DB_CONF_STR(flag) \
static int
{
int err;
/*
* If there are already pending "write" requests, queue this request in
* the pending list. Note that this function is called while the
* dlmgmt_rw_lock is held, so it is safe to access the global variables.
*/
(dlmgmt_db_req_head != NULL)) {
return (EINPROGRESS);
}
/*
* Log the error unless the request processing:
* - is successful;
* - is still in progress;
* - has failed with ENOENT because the active configuration
* file is not created yet;
*/
"operation on %s configuration failed: %s",
}
if (err == EINPROGRESS) {
if (err == 0)
return (EINPROGRESS);
}
return (err);
}
static int
{
int err = 0;
char file[MAXPATHLEN];
char newfile[MAXPATHLEN];
int nfd;
/*
* This can happen at boot when the file system is
* read-only. So add this request to the pending
* request list and start a retry thread.
*/
return (EINPROGRESS);
/*
* It is fine if the file keeping active configuration
* does not exist. This happens during a new reboot.
*/
if (!writeop)
return (ENOENT);
/*
* If this is an update request for the active
* configuration, create the file.
*/
} else {
return (errno);
}
}
if (writeop) {
return (err);
}
return (err);
}
}
if (writeop)
else
goto done;
goto done;
}
return (err);
}
return (0);
done:
if (err != 0)
}
return (err);
}
/*ARGSUSED*/
static void *
dlmgmt_db_update_thread(void *arg)
{
int err = 0;
if (err == EINPROGRESS) {
/*
* The filesystem is still read only. Go to sleep and
* try again.
*/
(void) sleep(5);
continue;
}
/*
* The filesystem is no longer read only. Continue processing
* and remove the request from the pending list.
*/
if (dlmgmt_db_req_tail == req) {
}
}
return (NULL);
}
static int
{
int i, len;
int err = 0;
char *curr;
char attr_name[MAXLINKATTRLEN];
size_t attr_buf_len = 0;
attr_name[0] = '\0';
char c = buf[i];
/*
* Move to the next character if there is no match and
* if we have not reached the last character.
*/
continue;
if (match) {
/*
* NUL-terminate the string pointed to by 'curr'.
*/
buf[i] = '\0';
if (*curr == '\0')
goto parse_fail;
}
/*
* We get here after we have processed the "<prop>="
* pattern. The pattern we are now interested in is
* "<val>;".
*/
if (c == '=')
goto parse_fail;
} else {
&attr_buf);
}
attr_name[0] = '\0';
} else if (attr_name[0] != '\0') {
/*
* Non-zero length attr_name and found_type of false
* indicates that we have not found the type for this
* attribute. The pattern now is "<type>,<val>;", we
* want the <type> part of the pattern.
*/
found_type = B_TRUE;
break;
}
}
if (!found_type)
goto parse_fail;
} else {
/*
* A zero length attr_name indicates we are looking
* at the beginning of a link attribute.
*/
if (c != '=')
goto parse_fail;
}
}
return (err);
return (-1);
}
static boolean_t
{
char tmpbuf[MAXLINELEN];
/*
* Use a copy of buf for parsing so that we can do whatever we want.
*/
/*
* Skip leading spaces, blank lines, and comments.
*/
for (i = 0; i < len; i++) {
break;
}
return (B_TRUE);
}
goto fail;
/*
* Find the link id and assign it to the link structure.
*/
goto fail;
goto fail;
/*
* Now find the list of link properties.
*/
goto fail;
goto fail;
return (B_TRUE);
fail:
/*
* Delete corrupted line.
*/
buf[0] = '\0';
return (B_FALSE);
}
static int
{
int err = 0;
char buf[MAXLINELEN];
/*
* find the link in the avl tree with the given linkid.
*/
/*
* This link has already been changed. This could
* happen if the request is pending because of
* read-only file-system. If so, we are done.
*/
return (0);
}
}
/*
* this is a comment line, write it out.
*/
continue;
}
case DLMGMT_DB_OP_WRITE:
/*
* For write operations, if the linkid of the link
* read from the file does not match the id of what
* req->ll_linkid points to, write out the buffer.
* Otherwise, generate a new line. If we get to the
* end and have not seen what req->ll_linkid points
* to, write it out then.
*/
} else {
}
break;
case DLMGMT_DB_OP_DELETE:
/*
* Delete is simple. If buf does not represent the
* link we're deleting, write it out.
*/
} else {
}
break;
case DLMGMT_DB_OP_READ:
default:
break;
}
}
/*
* If we get to the end of the file and have not seen what
* req->ll_linkid points to, write it out then.
*/
}
if (!done)
return (err);
}
/* ARGSUSED1 */
static int
{
char buf[MAXLINELEN];
int err = 0;
/*
* This loop processes each line of the configuration file.
*/
break;
}
/*
* Skip the comment line.
*/
if (link_in_file == NULL)
continue;
/*
* If any of the following conditions are met, this is
* a duplicate entry:
*
* 1. link2 (with the given name) and link2 (with the
* given id) are not the same link;
* 2. This is a persistent req and find the link with
* the given name and id. Note that persistent db
* is read before the active one.
* 3. Found the link with the given name and id but
* the link is already active.
*/
"entries in repository: link name %s "
} else {
}
} else {
}
}
return (err);
}
/*
* Generate an entry in the link database.
* Each entry has this format:
* <link id> <prop0>=<type>,<val>;...;<propn>=<type>,<val>;
*/
static void
{
char tmpbuf[MAXLINELEN];
char *ptr;
char *name_to_write = NULL;
/*
* The daemon does not keep any active link attribute. If this request
* is for active configuration, we are done.
*/
if (!persist)
goto done;
}
done:
return;
}
int
{
}
int
{
int err;
if (flags & DLMGMT_PERSIST) {
linkid, DLMGMT_PERSIST)) != 0) {
return (err);
}
}
if (flags & DLMGMT_ACTIVE) {
linkid, DLMGMT_ACTIVE)) != 0) &&
(flags & DLMGMT_PERSIST)) {
(void) dlmgmt_db_update(DLMGMT_DB_OP_DELETE,
return (err);
}
}
return (0);
}
/*
* Initialize the datalink <link name, linkid> mapping and the link's
* attributes list based on the configuration file /etc/dladm/datalink.conf
* and the active configuration cache file
*
* This function is called when the datalink-management service is started
* during reboot, and when the dlmgmtd daemon is restarted.
*/
int
{
char filename[MAXPATHLEN];
int err;
char *fmri, *c;
/*
* First derive the name of the cache file from the FMRI name. This
* cache name is used to keep active datalink configuration.
*/
if (debug) {
} else {
"service and should not be run from the command "
"line.");
return (EINVAL);
}
/*
* The FMRI name is in the form of
* prefix "svc:/" and replace '/' with '-'. The cache file
* name is in the form of "service:instance.cache".
*/
c++;
else
c = fmri;
for (c = filename; *c != '\0'; c++) {
if (*c == '/')
*c = '-';
}
}
goto done;
/*
* The temporary datalink.conf does not exist. This is
* the first boot. Mark all the physical links active.
*/
(void) dlmgmt_write_db_entry(
}
}
err = 0;
}
done:
return (err);
}