dcs.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
* 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 (c) 2000-2001 by Sun Microsystems, Inc.
* All rights reserved.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* This is the main file for the Domain Configuration Server (DCS).
*
* The DCS is a server that runs on a domain and communicates with
* a Domain Configuration Agent (DCA) running on a remote host. The
* DCA initiates DR requests that the DCS performs by calling the
* appropriate libcfgadm(3LIB) function.
*
* This file contains functions that receive and process the messages
* received from the DCA. It also handles the initialization of the
* server and is responsible for starting a concurrent session to
* handle each DR request.
*/
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <syslog.h>
#include <assert.h>
#include <signal.h>
#include <netdb.h>
#include <config_admin.h>
#include "dcs.h"
#include "remote_cfg.h"
#include "rdr_param_types.h"
#include "rdr_messages.h"
#include "rsrc_info.h"
typedef struct {
} dcs_ver_t;
/* initialization functions */
static void init_signals(void);
/* message processing functions */
/* message handling functions */
/* local callback functions */
/* utility functions */
/*
* Lookup table for handling different message types. This
* assumes the ordering of rdr_msg_opcode_t in remote_cfg.h.
* If this enum changes, the lookup table must be updated.
*
* The lookup table handles all _known_ opcodes >= 0. Unsupported
* opcodes, or opcodes that should not be received by the
* dispatcher are handled by the dcs_unknown_op() function.
*/
dcs_unknown_op, /* 0 is an invalid opcode */
dcs_ses_req, /* RDR_SES_REQ */
dcs_ses_estbl, /* RDR_SES_ESTBL */
dcs_ses_end, /* RDR_SES_END */
dcs_change_state, /* RDR_CONF_CHANGE_STATE */
dcs_private_func, /* RDR_CONF_PRIVATE_FUNC */
dcs_test, /* RDR_CONF_TEST */
dcs_list_ext, /* RDR_CONF_LIST_EXT */
dcs_help, /* RDR_CONF_HELP */
dcs_ap_id_cmp, /* RDR_CONF_AP_ID_CMP */
dcs_abort_cmd, /* RDR_CONF_ABORT_CMD */
dcs_unknown_op, /* RDR_CONF_CONFIRM_CALLBACK */
dcs_unknown_op, /* RDR_CONF_MSG_CALLBACK */
dcs_rsrc_info /* RDR_RSRC_INFO */
};
/*
* ver_supp[] is an array of the supported versions for the network
* transport protocol used by the DCA and DCS. Each item in the array
* is a pair: { major_version, minor_version }.
*
* The order of the array is significant. The first element should be
* the highest supported version and all successive elements should be
* strictly decreasing.
*/
{ 1, 0 }
};
#define DCS_CURR_VER ver_supp[0]
/*
* Global Data
*/
int standalone = 0; /* control standalone mode */
/*
* main:
*
* Initialize the DCS and then enter an infinite loop. This loop waits
* for connection requests to come and then establishes a connection.
* It dispatches the connection to be handled in a concurrent session.
*/
int
{
int opt;
int newfd;
/* initialize globals */
/* open log file with unique prefix */
/*
* Process command line args
*/
opterr = 0; /* disable getopt error messages */
switch (opt) {
case 'd': {
int usr_debug;
char *err_str;
/*
* The err_str parameter will be an
* empty string if successful.
*/
if (*err_str != '\0') {
optarg, "exiting");
(void) rdr_reject(dcsfd);
exit(1);
}
break;
}
case 'S':
standalone++;
break;
case 's': {
int usr_ses;
char *err_str;
if (usr_ses >= 1) {
} else {
char behavior_str[MAX_MSG_LEN];
"using default value (%d)", max_sessions);
}
break;
}
default:
(void) rdr_reject(dcsfd);
exit(1);
/* NOTREACHED */
break;
}
}
init_signals();
/* must be root */
if (geteuid() != 0) {
(void) rdr_reject(dcsfd);
exit(1);
}
/*
* Seed the random number generator for
* generating random session identifiers.
*/
/* initialize our transport endpoint */
(void) rdr_reject(dcsfd);
exit(1);
}
/*
* Main service loop
*/
for (;;) {
/* wait for a connection request */
}
continue;
}
/* attempt to connect */
continue;
}
/* process the session concurrently */
break;
}
}
return (1);
}
/*
* init_server:
*
* Perform all the operations that are required to initialize the
* transport endpoint used by the DCS. After this routine succeeds,
* the DCS is ready to accept session requests on its well known
* port.
*/
static int
{
struct sockaddr_storage ss;
struct sockaddr_in *sin;
struct sockaddr_in6 *sin6;
int req_port;
int act_port;
int init_status;
int num_sock_opts;
int sock_opts[] = { SO_REUSEADDR };
/*
* In standalone mode, we have to initialize the transport
* endpoint for our reserved port. In daemon mode, inetd
* starts the DCS and hands off STDIN_FILENO connected to
* our reserved port.
*/
if (standalone) {
/* in standalone mode, init fd for reserved port */
}
}
/*
* Look up our service to get the reserved port number
*/
/* use the known port if service wasn't found */
} else {
}
/* initialize our local address */
if (init_status != RDR_OK) {
return (-1);
}
case AF_INET:
break;
case AF_INET6:
/* sin6 already set correctly */
break;
default:
return (-1);
}
/* check that we got the requested port */
return (-1);
}
return (0);
}
/*
* init_signals:
*
* Initialize signals for the current session. All signals will be
* blocked with two possible exceptions. SIGINT is not blocked in
* standalone mode, and ses_init_signals() is called to selectively
* unblock any signals required to handle concurrent sessions.
*/
static void
init_signals(void)
{
/* block all signals */
sigfillset(&mask);
/* in standalone, allow user to abort */
if (standalone) {
}
}
/*
* dcs_dispatch_message:
*
* This function dispatches a message to the correct function. The
* correct handler is determined by the opcode field of the message
* header.
*/
int
{
/* get the current session information */
return (-1);
}
/* check the message */
if (invalid_msg(hdr)) {
return (-1);
}
/* save the current message */
/*
* hdr->message_opcode is unsigned so don't need
* to check for values less than zero
*/
return (-1);
}
/* dispatch the message */
return (-1);
}
return (0);
}
/*
* init_msg:
*
* Initialize the message header with information from the current
* session. Fields not set directly are initialized to zero.
*/
void
{
/* get the current session information */
return;
}
/* set the session information */
/* set the version being used */
}
/*
* invalid_msg:
*
* Check if the message is valid for the current session. This
* is accomplished by checking various information in the header
* against the information for the current session.
*/
static int
{
/* get the current session information */
return (-1);
}
/*
* Only perform the following checks if the message
* is not a session request. The information to check
* will not be set at the time a session request is
* received.
*/
/* check major and minor version */
return (-1);
}
/* check session identifiers */
return (-1);
}
}
return (0);
}
/*
* dcs_ses_req:
*
* Handle a session request message (RDR_SES_REQ).
*/
static int
{
int snd_status;
static char *op_name = "session request";
/* get the current session information */
return (-1);
}
/* make sure that a session hasn't been requested yet */
return (-1);
}
/* get the best matching version supported */
/* initialize session information */
/* prepare header information */
/* prepare session request specific data */
/* send the message */
if (snd_status == RDR_ABORTED) {
}
if (snd_status != RDR_OK) {
return (-1);
}
return (0);
}
/*
* dcs_ses_estbl:
*
* Handle a session establishment message (RDR_SES_ESTBL).
*/
/* ARGSUSED */
static int
{
/* get the current session information */
return (-1);
}
/*
* Make sure that a session has not been
* established yet, and that a session
* request has already been processed.
*/
return (-1);
}
/* get the best matching version supported */
/* end the session because protocol not supported */
hdr->minor_version);
return (-1);
}
return (0);
}
/*
* dcs_ses_end:
*
* Handle a session end message (RDR_SES_END).
*/
static int
{
int snd_status;
static char *op_name = "session end";
/* get the current session information */
return (-1);
}
/*
* Session end is valid from any state. However, only
* send back a reply if the error code is zero. A non-zero
* error code indicates that the session is being terminated
* under an error condition, and no acknowledgement is
* required.
*/
/* prepare header information */
/* return empty data - no information needed in reply */
if (snd_status == RDR_ABORTED) {
}
if (snd_status != RDR_OK) {
}
}
}
/*
* dcs_change_state:
*
* Handle a change state request message (RDR_CONF_CHANGE_STATE).
*/
static int
{
struct cfga_confirm local_conf_cb;
struct cfga_msg local_msg_cb;
int cfga_status = 0;
int snd_status;
char *err_str;
unsigned int curr_attempt;
unsigned int num_attempts;
char retry_msg[MAX_MSG_LEN];
static char *op_name = "config_change_state";
/* get the current session information */
return (-1);
}
/* make sure we have a session established */
return (-1);
}
/* initialize local confirm callback */
/* initialize local message callback */
/* verify retry value */
}
/* verify timeout value */
}
curr_attempt = 0;
while (curr_attempt < num_attempts) {
/* don't sleep the first time around */
if (curr_attempt != 0) {
/* log the error message and alert the user */
if (err_str) {
err_str);
err_str);
} else {
}
}
/* sleep with abort enabled */
/* log the retry attempt and alert the user */
}
/*
* Call into libcfgadm
*/
/*
* Retry only the operations that have a chance to
* succeed if retried. All libcfgadm errors not
* included below will always fail, regardless of
* a retry.
*/
if ((cfga_status != CFGA_BUSY) &&
(cfga_status != CFGA_SYSTEM_BUSY) &&
(cfga_status != CFGA_ERROR)) {
break;
}
/* prepare for another attempt */
++curr_attempt;
}
/* log any libcfgadm errors */
if (cfga_status != CFGA_OK) {
if (err_str) {
}
}
/* prepare header information */
/* send the message */
if (snd_status == RDR_ABORTED) {
}
if (snd_status != RDR_OK) {
}
/* clean up */
}
}
/*
* dcs_private_func:
*
* Handle a private function request message (RDR_CONF_PRIVATE_FUNC).
*/
static int
{
struct cfga_confirm local_conf_cb;
struct cfga_msg local_msg_cb;
int cfga_status;
int snd_status;
char *err_str;
static char *op_name = "config_private_func";
/* get the current session information */
return (-1);
}
/* make sure we have a session established */
return (-1);
}
/* initialize local confirm callback */
/* initialize local message callback */
/*
* Call into libcfgadm
*/
/* log any libcfgadm errors */
if (cfga_status != CFGA_OK) {
if (err_str) {
}
}
/* prepare header information */
/* send the message */
if (snd_status == RDR_ABORTED) {
}
if (snd_status != RDR_OK) {
}
}
}
/*
* dcs_test:
*
* Handle a test request message (RDR_CONF_TEST).
*/
static int
{
struct cfga_msg local_msg_cb;
int cfga_status;
int snd_status;
char *err_str;
static char *op_name = "config_test";
/* get the current session information */
return (-1);
}
/* make sure we have a session established */
return (-1);
}
/* initialize local message callback */
/*
* Call into libcfgadm
*/
/* log any libcfgadm errors */
if (cfga_status != CFGA_OK) {
if (err_str) {
}
}
/* prepare header information */
/* send the message */
if (snd_status == RDR_ABORTED) {
}
if (snd_status != RDR_OK) {
}
}
}
/*
* dcs_list_ext:
*
* Handle a list request message (RDR_CONF_LIST_EXT).
*/
static int
{
int cfga_status;
int snd_status;
char *err_str;
static char *op_name = "config_list_ext";
/* get the current session information */
return (-1);
}
/* make sure we have a session established */
return (-1);
}
/*
* Make sure that we can retrieve the data
* from libcfgadm. If not, report the error.
*/
return (-1);
}
/*
* Call into libcfgadm
*/
/*
* Log any libcfgadm errors at a low priority level.
* Since a status request does not modify the system
* in any way, we do not need to worry about these
* errors here on the host.
*/
if (cfga_status != CFGA_OK) {
if (err_str) {
}
}
/*
* Filter ap ids to return only appropriate information
*/
/* if all aps were filtered out, return an error */
}
/* calculate the sort order */
if (cfga_status == CFGA_OK) {
}
}
/* ensure that nlist is 0 for errors */
if (cfga_status != CFGA_OK) {
}
/* prepare header information */
/* send the message */
if (snd_status == RDR_ABORTED) {
}
if (snd_status != RDR_OK) {
}
}
}
}
/*
* dcs_help:
*
* Handle a help request message (RDR_CONF_HELP).
*/
static int
{
struct cfga_msg local_msg_cb;
int cfga_status;
int snd_status;
char *err_str;
static char *op_name = "config_help";
/* get the current session information */
return (-1);
}
/* make sure we have a session established */
return (-1);
}
/* initialize local message callback */
/*
* Call into libcfgadm
*/
/*
* Log any libcfgadm errors at a low priority level.
* Since a help request does not modify the system
* in any way, we do not need to worry about these
* errors here on the host.
*/
if (cfga_status != CFGA_OK) {
if (err_str) {
}
}
/* prepare header information */
/* send the message */
if (snd_status == RDR_ABORTED) {
}
if (snd_status != RDR_OK) {
}
}
/*
* dcs_ap_id_cmp:
*
* Handle an attachment point comparison request message (RDR_AP_ID_CMP).
*/
static int
{
int snd_status;
int cmp_result;
static char *op_name = "config_ap_id_cmp";
/* get the current session information */
return (-1);
}
/* make sure we have a session established */
return (-1);
}
/*
* Call into libcfgadm
*/
/* prepare header information */
/*
* Return result of comparison as error code.
* Since all values are valid, it is impossible
* to report an error.
*/
/* send the message */
if (snd_status == RDR_ABORTED) {
}
if (snd_status != RDR_OK) {
}
}
/*
* dcs_abort_cmd:
*
* Handle an abort request message (RDR_CONF_ABORT_CMD).
*/
/* ARGSUSED */
static int
{
int op_status = RDR_SUCCESS;
int snd_status;
static char *op_name = "abort command";
/* get the current session information */
return (-1);
}
if (op_status == -1) {
}
/* prepare header information */
/* send the message */
if (snd_status == RDR_ABORTED) {
}
if (snd_status != RDR_OK) {
}
}
/*
* dcs_rsrc_info:
*
* Handle a resource info request message (RDR_RSRC_INFO).
*/
static int
{
int rsrc_status;
int snd_status;
static char *op_name = "resource info init";
/* get the current session information */
return (-1);
}
/* make sure we have a session established */
return (-1);
}
/*
* Request resource info data.
*/
/* log errors */
if (rsrc_status != RI_SUCCESS) {
}
/* prepare header information */
/* send the message */
if (snd_status == RDR_ABORTED) {
}
if (snd_status != RDR_OK) {
}
}
/*
* dcs_unknown_op:
*
* Handle all unknown requests.
*/
/* ARGSUSED */
static int
{
/* get the current session information */
return (-1);
}
return (-1);
}
/*
* dcs_confirm_callback:
*
* Perform a confirm callback and wait for the reply. As defined
* in the config_admin(3CFGADM) man page, 1 is returned if the
* operation should be allowed to continue and 0 otherwise.
*/
static int
{
struct cfga_confirm *cb_data;
int snd_status;
int rcv_status;
static char *op_name = "confirm callback";
/* sanity check */
if (appdata_ptr == NULL) {
return (0);
}
/* get the current session information */
return (0);
}
/* prepare header information */
/* prepare confirm callback specific data */
/* send the message */
if (snd_status == RDR_ABORTED) {
}
if (snd_status != RDR_OK) {
return (0);
}
/*
* Wait for response
*/
if (rcv_status != RDR_OK) {
return (0);
}
/*
* Perform several checks to see if we have a
* valid response to the confirm callback.
*/
if (invalid_msg(&reply_hdr)) {
return (0);
}
/* check the opcode and type */
return (0);
}
/* check for incorrect callback id */
return (0);
}
/*
* Got back valid response: return the user's answer
*/
}
/*
* dcs_message_callback:
*
* Perform a message callback to display a string to the user.
*
* Note: There is no documentation about possible return values
* for the message callback. It is assumed that the value returned
* is ignored, so 0 is returned for all cases.
*/
static int
{
int snd_status;
static char *op_name = "message callback";
/* sanity check */
if (appdata_ptr == NULL) {
return (0);
}
/* get the current session information */
return (0);
}
/* prepare header information */
/* prepare message callback specific data */
/* send the message */
if (snd_status == RDR_ABORTED) {
}
if (snd_status != RDR_OK) {
}
return (0);
}
/*
* resolve_version:
*
* Consult the list of supported versions and find the highest supported
* version that is less than or equal to the version requested in the
* parameters. This assumes that the list of supported versions is ordered
* so that the highest supported version is the first element, and that
* the versions are strictly decreasing.
*/
static dcs_ver_t
{
int i;
int num_vers;
/* default to the lowest version */
for (i = 0; i < num_vers; i++) {
/*
* The major version matches and the
* minor version either matches, or
* is the best match that we have.
*/
break;
}
/*
* The requested major version is larger than
* the current version we are checking. There
* is not going to be a better match.
*/
break;
}
}
return (act_ver);
}
/*
* filter_list_data:
*
* Check a list of cfga_list_data_t structures to filter out the ones
* that don't have other-read permissions. All valid entries are placed
* at the beginning of the array and the count of entries is updated.
*/
static void
{
int num_aps;
int num_aps_ret;
int curr_ap;
int next_aval;
int end_block;
int block_size;
"RDR_PRIVILEGED" : "RDR_NOT_PRIVILEGED");
/*
* Check if the user has priviledged access
* to view all attachment points
*/
if (perm == RDR_PRIVILEGED) {
return;
}
if (*nlistp < 0) {
*nlistp = 0;
}
/*
* No priviledged access, check each attachment point to
* see if the user has access (other:read) to view it.
*/
next_aval = 0;
num_aps_ret = 0;
curr_ap = 0;
/*
* Use a simple algorithm to compact the array so that
* all attachment points that can be viewed are at the
* beginning of the array. Adjust the count of the
* attachment points accordingly.
*/
/* check for unrestricted read permission */
/*
* Check if this is the beginning of a
* block of consecutive ap ids that can
* be returned.
*/
/* search until the end of the block */
end_block++;
} else {
break;
}
}
/* make sure a copy is necessary */
/* copy the block of ap ids all at once */
block_size * sizeof (cfga_list_data_t));
}
/* move past the copied block */
next_aval += block_size;
} else {
curr_ap++;
}
}
*nlistp);
/*
* return the number of aps that have the correct
* access permissions.
*/
*nlistp = num_aps_ret;
}
/*
* generate_sort_order:
*
* Determine the sort order of an array of cfga_list_data_t structures
* and create an array of rdr_list_t structures that contain the original
* elements tagged with the sort order.
*
* This function is used to eliminate unnecessary network traffic that
* might occur if the client needs the output of config_list_ext(3CFGADM)
* sorted. Since a comparison is performed in a platform specific manner
* using config_ap_id_cmp(3CFGADM), a client must establish a new session
* for each comparison. For a long lists of attachment points, this can
* slow down a simple list_ext operation significantly. With the sort
* information included in the array of rdr_list_t structures, the client
* can perform the sort operation locally, thus eliminating a great deal
* of network traffic.
*/
static rdr_list_t *
{
int curr_ap;
if (nlist <= 0) {
return (NULL);
}
/* create our new array */
return (NULL);
}
/* copy over the elements, preserving the original order */
}
/* handle a one element list */
if (nlist == 1) {
datalp[0].sort_order = 0;
return (datalp);
}
/* sort the cfga_list_data_t array */
/* process each item in the original list */
/* look up the sort order in the sorted list */
/* found a match */
} else {
/*
* Should never get here. Since we did a
* direct copy of the array, we should always
* be able to find the ap id that we were
* looking for.
*/
"ap id in the sorted list");
}
}
return (datalp);
}
/*
* ldata_compare:
*
* Compare the two inputs to produce a strcmp(3C) style result. It uses
* config_ap_id_cmp(3CFGADM) to perform the comparison.
*
* This function is passed to qsort(3C) in generate_sort_order() to sort a
* list of attachment points.
*/
static int
{
}
/*
* basename:
*
* Find short path name of a full path name. If a short path name
* is passed in, the original pointer is returned.
*/
static char *
{
char *sp;
return (sp + 1);
}
return (cp);
}