2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A/*
2N/A * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
2N/A * Use is subject to license terms.
2N/A */
2N/A
2N/A#include <sys/types.h>
2N/A#include <sys/ctfs.h>
2N/A#include <sys/contract/process.h>
2N/A#include <sys/socket.h>
2N/A#include <sys/time.h>
2N/A#include <sys/wait.h>
2N/A#include <fcntl.h>
2N/A#include <libcontract.h>
2N/A#include <libcontract_priv.h>
2N/A#include <unistd.h>
2N/A#include <stdio.h>
2N/A#include <stdlib.h>
2N/A#include <string.h>
2N/A
2N/A#include "dhcpagent_ipc.h"
2N/A#include "dhcpagent_util.h"
2N/A
2N/A/*
2N/A * Strings returned by dhcp_status_hdr_string() and
2N/A * dhcp_status_reply_to_string(). The first define is the header line, and
2N/A * the second defines line printed underneath.
2N/A * The spacing of fields must match.
2N/A */
2N/A#define DHCP_STATUS_HDR "Interface State Sent Recv Declined Flags\n"
2N/A#define DHCP_STATUS_STR "%-10s %-12s %5d %5d %9d "
2N/A
2N/Astatic const char *time_to_string(time_t abs_time);
2N/A
2N/A/*
2N/A * dhcp_state_to_string(): given a state, provides the state's name
2N/A *
2N/A * input: DHCPSTATE: the state to get the name of
2N/A * output: const char *: the state's name
2N/A */
2N/A
2N/Aconst char *
2N/Adhcp_state_to_string(DHCPSTATE state)
2N/A{
2N/A const char *states[] = {
2N/A "INIT",
2N/A "SELECTING",
2N/A "REQUESTING",
2N/A "PRE_BOUND",
2N/A "BOUND",
2N/A "RENEWING",
2N/A "REBINDING",
2N/A "INFORMATION",
2N/A "INIT_REBOOT",
2N/A "ADOPTING",
2N/A "INFORM_SENT",
2N/A "DECLINING",
2N/A "RELEASING"
2N/A };
2N/A
2N/A if (state < 0 || state >= DHCP_NSTATES)
2N/A return ("<unknown>");
2N/A
2N/A return (states[state]);
2N/A}
2N/A
2N/Astatic int
2N/Ainit_template(void)
2N/A{
2N/A int fd;
2N/A int err = 0;
2N/A
2N/A fd = open64(CTFS_ROOT "/process/template", O_RDWR);
2N/A if (fd == -1)
2N/A return (-1);
2N/A
2N/A /*
2N/A * Deliver no events, don't inherit, and allow it to be orphaned.
2N/A */
2N/A err |= ct_tmpl_set_critical(fd, 0);
2N/A err |= ct_tmpl_set_informative(fd, 0);
2N/A err |= ct_pr_tmpl_set_fatal(fd, CT_PR_EV_HWERR);
2N/A err |= ct_pr_tmpl_set_param(fd, CT_PR_PGRPONLY | CT_PR_REGENT);
2N/A if (err != 0 || ct_tmpl_activate(fd) != 0) {
2N/A (void) close(fd);
2N/A return (-1);
2N/A }
2N/A
2N/A return (fd);
2N/A}
2N/A
2N/A/*
2N/A * dhcp_start_agent(): starts the agent if not already running
2N/A *
2N/A * input: int: number of seconds to wait for agent to start (-1 is forever)
2N/A * output: int: 0 on success, -1 on failure
2N/A */
2N/A
2N/Aint
2N/Adhcp_start_agent(int timeout)
2N/A{
2N/A int error;
2N/A time_t start_time = time(NULL);
2N/A dhcp_ipc_request_t *request;
2N/A dhcp_ipc_reply_t *reply;
2N/A int ctfd;
2N/A pid_t childpid;
2N/A ctid_t ct;
2N/A
2N/A /*
2N/A * just send a dummy request to the agent to find out if it's
2N/A * up. we do this instead of directly connecting to it since
2N/A * we want to make sure we follow its IPC conventions
2N/A * (otherwise, it will log warnings to syslog).
2N/A */
2N/A
2N/A request = dhcp_ipc_alloc_request(DHCP_PING, "", NULL, 0,
2N/A DHCP_TYPE_NONE);
2N/A if (request == NULL)
2N/A return (-1);
2N/A
2N/A error = dhcp_ipc_make_request(request, &reply, 0);
2N/A if (error == 0) {
2N/A free(reply);
2N/A free(request);
2N/A return (0);
2N/A }
2N/A if (error != DHCP_IPC_E_CONNECT)
2N/A goto fail;
2N/A
2N/A if ((ctfd = init_template()) == -1)
2N/A goto fail;
2N/A
2N/A childpid = fork();
2N/A
2N/A (void) ct_tmpl_clear(ctfd);
2N/A (void) close(ctfd);
2N/A
2N/A switch (childpid) {
2N/A case -1:
2N/A goto fail;
2N/A
2N/A case 0:
2N/A (void) execl(DHCP_AGENT_PATH, DHCP_AGENT_PATH, (char *)0);
2N/A _exit(EXIT_FAILURE);
2N/A
2N/A default:
2N/A break;
2N/A }
2N/A
2N/A /* wait for the daemon to run and then abandon the contract */
2N/A (void) waitpid(childpid, NULL, 0);
2N/A
2N/A if (contract_latest(&ct) != -1)
2N/A (void) contract_abandon_id(ct);
2N/A
2N/A while ((timeout != -1) && (time(NULL) - start_time < timeout)) {
2N/A error = dhcp_ipc_make_request(request, &reply, 0);
2N/A if (error == 0) {
2N/A free(reply);
2N/A free(request);
2N/A return (0);
2N/A } else if (error != DHCP_IPC_E_CONNECT)
2N/A break;
2N/A (void) sleep(1);
2N/A }
2N/A
2N/Afail:
2N/A free(request);
2N/A return (-1);
2N/A}
2N/A
2N/A/*
2N/A * dhcp_status_hdr_string(): Return a string suitable to use as the header
2N/A * when printing DHCP_STATUS reply.
2N/A * output: const char *: newline terminated printable string
2N/A */
2N/Aconst char *
2N/Adhcp_status_hdr_string(void)
2N/A{
2N/A return (DHCP_STATUS_HDR);
2N/A}
2N/A
2N/A/*
2N/A * time_to_string(): Utility routine for printing time
2N/A *
2N/A * input: time_t *: time_t to stringify
2N/A * output: const char *: printable time
2N/A */
2N/Astatic const char *
2N/Atime_to_string(time_t abs_time)
2N/A{
2N/A static char time_buf[24];
2N/A time_t tm = abs_time;
2N/A
2N/A if (tm == DHCP_PERM)
2N/A return ("Never");
2N/A
2N/A if (strftime(time_buf, sizeof (time_buf), "%m/%d/%Y %R",
2N/A localtime(&tm)) == 0)
2N/A return ("<unknown>");
2N/A
2N/A return (time_buf);
2N/A}
2N/A
2N/A/*
2N/A * dhcp_status_reply_to_string(): Return DHCP IPC reply of type DHCP_STATUS
2N/A * as a printable string
2N/A *
2N/A * input: dhcp_reply_t *: contains the status structure to print
2N/A * output: const char *: newline terminated printable string
2N/A */
2N/Aconst char *
2N/Adhcp_status_reply_to_string(dhcp_ipc_reply_t *reply)
2N/A{
2N/A static char str[1024];
2N/A size_t reply_size;
2N/A dhcp_status_t *status;
2N/A
2N/A status = dhcp_ipc_get_data(reply, &reply_size, NULL);
2N/A if (reply_size < DHCP_STATUS_VER1_SIZE)
2N/A return ("<Internal error: status msg size>\n");
2N/A
2N/A (void) snprintf(str, sizeof (str), DHCP_STATUS_STR,
2N/A status->if_name, dhcp_state_to_string(status->if_state),
2N/A status->if_sent, status->if_recv, status->if_bad_offers);
2N/A
2N/A if (status->if_dflags & DHCP_IF_PRIMARY)
2N/A (void) strlcat(str, "[PRIMARY] ", sizeof (str));
2N/A
2N/A if (status->if_dflags & DHCP_IF_BOOTP)
2N/A (void) strlcat(str, "[BOOTP] ", sizeof (str));
2N/A
2N/A if (status->if_dflags & DHCP_IF_FAILED)
2N/A (void) strlcat(str, "[FAILED] ", sizeof (str));
2N/A
2N/A if (status->if_dflags & DHCP_IF_BUSY)
2N/A (void) strlcat(str, "[BUSY] ", sizeof (str));
2N/A
2N/A if (status->if_dflags & DHCP_IF_V6)
2N/A (void) strlcat(str, "[V6] ", sizeof (str));
2N/A
2N/A (void) strlcat(str, "\n", sizeof (str));
2N/A
2N/A switch (status->if_state) {
2N/A case BOUND:
2N/A case RENEWING:
2N/A case REBINDING:
2N/A break;
2N/A default:
2N/A return (str);
2N/A }
2N/A
2N/A (void) strlcat(str, "(Began, Expires, Renew) = (", sizeof (str));
2N/A (void) strlcat(str, time_to_string(status->if_began), sizeof (str));
2N/A (void) strlcat(str, ", ", sizeof (str));
2N/A (void) strlcat(str, time_to_string(status->if_lease), sizeof (str));
2N/A (void) strlcat(str, ", ", sizeof (str));
2N/A (void) strlcat(str, time_to_string(status->if_t1), sizeof (str));
2N/A (void) strlcat(str, ")\n", sizeof (str));
2N/A return (str);
2N/A}