1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 
  26 #include <sys/types.h>
  27 #include <sys/ctfs.h>
  28 #include <sys/contract/process.h>
  29 #include <sys/socket.h>
  30 #include <sys/time.h>
  31 #include <sys/wait.h>
  32 #include <fcntl.h>
  33 #include <libcontract.h>
  34 #include <libcontract_priv.h>
  35 #include <unistd.h>
  36 #include <stdio.h>
  37 #include <stdlib.h>
  38 #include <string.h>
  39 
  40 #include "dhcpagent_ipc.h"
  41 #include "dhcpagent_util.h"
  42 
  43 /*
  44  * Strings returned by dhcp_status_hdr_string() and
  45  * dhcp_status_reply_to_string(). The first define is the header line, and
  46  * the second defines line printed underneath.
  47  * The spacing of fields must match.
  48  */
  49 #define DHCP_STATUS_HDR "Interface  State         Sent  Recv  Declined  Flags\n"
  50 #define DHCP_STATUS_STR "%-10s %-12s %5d %5d %9d  "
  51 
  52 static const char *time_to_string(time_t abs_time);
  53 
  54 /*
  55  * dhcp_state_to_string(): given a state, provides the state's name
  56  *
  57  *    input: DHCPSTATE: the state to get the name of
  58  *   output: const char *: the state's name
  59  */
  60 
  61 const char *
  62 dhcp_state_to_string(DHCPSTATE state)
  63 {
  64         const char *states[] = {
  65                 "INIT",
  66                 "SELECTING",
  67                 "REQUESTING",
  68                 "PRE_BOUND",
  69                 "BOUND",
  70                 "RENEWING",
  71                 "REBINDING",
  72                 "INFORMATION",
  73                 "INIT_REBOOT",
  74                 "ADOPTING",
  75                 "INFORM_SENT",
  76                 "DECLINING",
  77                 "RELEASING"
  78         };
  79 
  80         if (state < 0 || state >= DHCP_NSTATES)
  81                 return ("<unknown>");
  82 
  83         return (states[state]);
  84 }
  85 
  86 static int
  87 init_template(void)
  88 {
  89         int fd;
  90         int err = 0;
  91 
  92         fd = open64(CTFS_ROOT "/process/template", O_RDWR);
  93         if (fd == -1)
  94                 return (-1);
  95 
  96         /*
  97          * Deliver no events, don't inherit, and allow it to be orphaned.
  98          */
  99         err |= ct_tmpl_set_critical(fd, 0);
 100         err |= ct_tmpl_set_informative(fd, 0);
 101         err |= ct_pr_tmpl_set_fatal(fd, CT_PR_EV_HWERR);
 102         err |= ct_pr_tmpl_set_param(fd, CT_PR_PGRPONLY | CT_PR_REGENT);
 103         if (err != 0 || ct_tmpl_activate(fd) != 0) {
 104                 (void) close(fd);
 105                 return (-1);
 106         }
 107 
 108         return (fd);
 109 }
 110 
 111 /*
 112  * dhcp_start_agent(): starts the agent if not already running
 113  *
 114  *   input: int: number of seconds to wait for agent to start (-1 is forever)
 115  *  output: int: 0 on success, -1 on failure
 116  */
 117 
 118 int
 119 dhcp_start_agent(int timeout)
 120 {
 121         int                     error;
 122         time_t                  start_time = time(NULL);
 123         dhcp_ipc_request_t      *request;
 124         dhcp_ipc_reply_t        *reply;
 125         int                     ctfd;
 126         pid_t                   childpid;
 127         ctid_t                  ct;
 128 
 129         /*
 130          * just send a dummy request to the agent to find out if it's
 131          * up.  we do this instead of directly connecting to it since
 132          * we want to make sure we follow its IPC conventions
 133          * (otherwise, it will log warnings to syslog).
 134          */
 135 
 136         request = dhcp_ipc_alloc_request(DHCP_PING, "", NULL, 0,
 137             DHCP_TYPE_NONE);
 138         if (request == NULL)
 139                 return (-1);
 140 
 141         error = dhcp_ipc_make_request(request, &reply, 0);
 142         if (error == 0) {
 143                 free(reply);
 144                 free(request);
 145                 return (0);
 146         }
 147         if (error != DHCP_IPC_E_CONNECT)
 148                 goto fail;
 149 
 150         if ((ctfd = init_template()) == -1)
 151                 goto fail;
 152 
 153         childpid = fork();
 154 
 155         (void) ct_tmpl_clear(ctfd);
 156         (void) close(ctfd);
 157 
 158         switch (childpid) {
 159         case -1:
 160                 goto fail;
 161 
 162         case  0:
 163                 (void) execl(DHCP_AGENT_PATH, DHCP_AGENT_PATH, (char *)0);
 164                 _exit(EXIT_FAILURE);
 165 
 166         default:
 167                 break;
 168         }
 169 
 170         /* wait for the daemon to run and then abandon the contract */
 171         (void) waitpid(childpid, NULL, 0);
 172 
 173         if (contract_latest(&ct) != -1)
 174                 (void) contract_abandon_id(ct);
 175 
 176         while ((timeout != -1) && (time(NULL) - start_time < timeout)) {
 177                 error = dhcp_ipc_make_request(request, &reply, 0);
 178                 if (error == 0) {
 179                         free(reply);
 180                         free(request);
 181                         return (0);
 182                 } else if (error != DHCP_IPC_E_CONNECT)
 183                         break;
 184                 (void) sleep(1);
 185         }
 186 
 187 fail:
 188         free(request);
 189         return (-1);
 190 }
 191 
 192 /*
 193  * dhcp_status_hdr_string(): Return a string suitable to use as the header
 194  *                           when printing DHCP_STATUS reply.
 195  *  output: const char *: newline terminated printable string
 196  */
 197 const char *
 198 dhcp_status_hdr_string(void)
 199 {
 200         return (DHCP_STATUS_HDR);
 201 }
 202 
 203 /*
 204  * time_to_string(): Utility routine for printing time
 205  *
 206  *   input: time_t *: time_t to stringify
 207  *  output: const char *: printable time
 208  */
 209 static const char *
 210 time_to_string(time_t abs_time)
 211 {
 212         static char time_buf[24];
 213         time_t tm = abs_time;
 214 
 215         if (tm == DHCP_PERM)
 216                 return ("Never");
 217 
 218         if (strftime(time_buf, sizeof (time_buf), "%m/%d/%Y %R",
 219             localtime(&tm)) == 0)
 220                 return ("<unknown>");
 221 
 222         return (time_buf);
 223 }
 224 
 225 /*
 226  * dhcp_status_reply_to_string(): Return DHCP IPC reply of type DHCP_STATUS
 227  *                                as a printable string
 228  *
 229  *   input: dhcp_reply_t *: contains the status structure to print
 230  *  output: const char *: newline terminated printable string
 231  */
 232 const char *
 233 dhcp_status_reply_to_string(dhcp_ipc_reply_t *reply)
 234 {
 235         static char str[1024];
 236         size_t reply_size;
 237         dhcp_status_t *status;
 238 
 239         status = dhcp_ipc_get_data(reply, &reply_size, NULL);
 240         if (reply_size < DHCP_STATUS_VER1_SIZE)
 241                 return ("<Internal error: status msg size>\n");
 242 
 243         (void) snprintf(str, sizeof (str), DHCP_STATUS_STR,
 244             status->if_name, dhcp_state_to_string(status->if_state),
 245             status->if_sent, status->if_recv, status->if_bad_offers);
 246 
 247         if (status->if_dflags & DHCP_IF_PRIMARY)
 248                 (void) strlcat(str, "[PRIMARY] ", sizeof (str));
 249 
 250         if (status->if_dflags & DHCP_IF_BOOTP)
 251                 (void) strlcat(str, "[BOOTP] ", sizeof (str));
 252 
 253         if (status->if_dflags & DHCP_IF_FAILED)
 254                 (void) strlcat(str, "[FAILED] ", sizeof (str));
 255 
 256         if (status->if_dflags & DHCP_IF_BUSY)
 257                 (void) strlcat(str, "[BUSY] ", sizeof (str));
 258 
 259         if (status->if_dflags & DHCP_IF_V6)
 260                 (void) strlcat(str, "[V6] ", sizeof (str));
 261 
 262         (void) strlcat(str, "\n", sizeof (str));
 263 
 264         switch (status->if_state) {
 265         case BOUND:
 266         case RENEWING:
 267         case REBINDING:
 268                 break;
 269         default:
 270                 return (str);
 271         }
 272 
 273         (void) strlcat(str, "(Began, Expires, Renew) = (", sizeof (str));
 274         (void) strlcat(str, time_to_string(status->if_began), sizeof (str));
 275         (void) strlcat(str, ", ", sizeof (str));
 276         (void) strlcat(str, time_to_string(status->if_lease), sizeof (str));
 277         (void) strlcat(str, ", ", sizeof (str));
 278         (void) strlcat(str, time_to_string(status->if_t1), sizeof (str));
 279         (void) strlcat(str, ")\n", sizeof (str));
 280         return (str);
 281 }