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 #include <zone.h>
  40 
  41 #include "dhcpagent_ipc.h"
  42 #include "dhcpagent_util.h"
  43 
  44 /*
  45  * Strings returned by dhcp_status_hdr_string() and
  46  * dhcp_status_reply_to_string(). The first define is the header line, and
  47  * the second defines line printed underneath.
  48  * The spacing of fields must match.
  49  */
  50 #define DHCP_STATUS_HDR "Interface  State         Sent  Recv  Declined  Flags\n"
  51 #define DHCP_STATUS_STR "%-10s %-12s %5d %5d %9d  "
  52 
  53 static const char *time_to_string(time_t abs_time);
  54 
  55 /*
  56  * dhcp_state_to_string(): given a state, provides the state's name
  57  *
  58  *    input: DHCPSTATE: the state to get the name of
  59  *   output: const char *: the state's name
  60  */
  61 
  62 const char *
  63 dhcp_state_to_string(DHCPSTATE state)
  64 {
  65         const char *states[] = {
  66                 "INIT",
  67                 "SELECTING",
  68                 "REQUESTING",
  69                 "PRE_BOUND",
  70                 "BOUND",
  71                 "RENEWING",
  72                 "REBINDING",
  73                 "INFORMATION",
  74                 "INIT_REBOOT",
  75                 "ADOPTING",
  76                 "INFORM_SENT",
  77                 "DECLINING",
  78                 "RELEASING"
  79         };
  80 
  81         if (state < 0 || state >= DHCP_NSTATES)
  82                 return ("<unknown>");
  83 
  84         return (states[state]);
  85 }
  86 
  87 static int
  88 init_template(void)
  89 {
  90         int fd;
  91         int err = 0;
  92 
  93         fd = open64(CTFS_ROOT "/process/template", O_RDWR);
  94         if (fd == -1)
  95                 return (-1);
  96 
  97         /*
  98          * Deliver no events, don't inherit, and allow it to be orphaned.
  99          */
 100         err |= ct_tmpl_set_critical(fd, 0);
 101         err |= ct_tmpl_set_informative(fd, 0);
 102         err |= ct_pr_tmpl_set_fatal(fd, CT_PR_EV_HWERR);
 103         err |= ct_pr_tmpl_set_param(fd, CT_PR_PGRPONLY | CT_PR_REGENT);
 104         if (err != 0 || ct_tmpl_activate(fd) != 0) {
 105                 (void) close(fd);
 106                 return (-1);
 107         }
 108 
 109         return (fd);
 110 }
 111 
 112 /*
 113  * dhcp_start_agent(): starts the agent if not already running
 114  *
 115  *   input: int: number of seconds to wait for agent to start (-1 is forever)
 116  *  output: int: 0 on success, -1 on failure
 117  */
 118 
 119 int
 120 dhcp_start_agent(int timeout)
 121 {
 122         int                     error;
 123         time_t                  start_time = time(NULL);
 124         dhcp_ipc_request_t      *request;
 125         dhcp_ipc_reply_t        *reply;
 126         int                     ctfd;
 127         pid_t                   childpid;
 128         ctid_t                  ct;
 129         char                    dhcpcmd[MAXPATHLEN];
 130         const char              *zroot = zone_get_nroot();
 131 
 132         /* Prepend the root of the native code in the brand to the command */
 133         (void) snprintf(dhcpcmd, sizeof (dhcpcmd), "%s%s", zroot != NULL ?
 134             zroot : "", DHCP_AGENT_PATH);
 135 
 136         /*
 137          * just send a dummy request to the agent to find out if it's
 138          * up.  we do this instead of directly connecting to it since
 139          * we want to make sure we follow its IPC conventions
 140          * (otherwise, it will log warnings to syslog).
 141          */
 142 
 143         request = dhcp_ipc_alloc_request(DHCP_PING, "", NULL, 0,
 144             DHCP_TYPE_NONE);
 145         if (request == NULL)
 146                 return (-1);
 147 
 148         error = dhcp_ipc_make_request(request, &reply, 0);
 149         if (error == 0) {
 150                 free(reply);
 151                 free(request);
 152                 return (0);
 153         }
 154         if (error != DHCP_IPC_E_CONNECT)
 155                 goto fail;
 156 
 157         if ((ctfd = init_template()) == -1)
 158                 goto fail;
 159 
 160         childpid = fork();
 161 
 162         (void) ct_tmpl_clear(ctfd);
 163         (void) close(ctfd);
 164 
 165         switch (childpid) {
 166         case -1:
 167                 goto fail;
 168 
 169         case  0:
 170                 (void) execl(dhcpcmd, dhcpcmd, (char *)0);
 171                 _exit(EXIT_FAILURE);
 172 
 173         default:
 174                 break;
 175         }
 176 
 177         /* wait for the daemon to run and then abandon the contract */
 178         (void) waitpid(childpid, NULL, 0);
 179 
 180         if (contract_latest(&ct) != -1)
 181                 (void) contract_abandon_id(ct);
 182 
 183         while ((timeout != -1) && (time(NULL) - start_time < timeout)) {
 184                 error = dhcp_ipc_make_request(request, &reply, 0);
 185                 if (error == 0) {
 186                         free(reply);
 187                         free(request);
 188                         return (0);
 189                 } else if (error != DHCP_IPC_E_CONNECT)
 190                         break;
 191                 (void) sleep(1);
 192         }
 193 
 194 fail:
 195         free(request);
 196         return (-1);
 197 }
 198 
 199 /*
 200  * dhcp_status_hdr_string(): Return a string suitable to use as the header
 201  *                           when printing DHCP_STATUS reply.
 202  *  output: const char *: newline terminated printable string
 203  */
 204 const char *
 205 dhcp_status_hdr_string(void)
 206 {
 207         return (DHCP_STATUS_HDR);
 208 }
 209 
 210 /*
 211  * time_to_string(): Utility routine for printing time
 212  *
 213  *   input: time_t *: time_t to stringify
 214  *  output: const char *: printable time
 215  */
 216 static const char *
 217 time_to_string(time_t abs_time)
 218 {
 219         static char time_buf[24];
 220         time_t tm = abs_time;
 221 
 222         if (tm == DHCP_PERM)
 223                 return ("Never");
 224 
 225         if (strftime(time_buf, sizeof (time_buf), "%m/%d/%Y %R",
 226             localtime(&tm)) == 0)
 227                 return ("<unknown>");
 228 
 229         return (time_buf);
 230 }
 231 
 232 /*
 233  * dhcp_status_reply_to_string(): Return DHCP IPC reply of type DHCP_STATUS
 234  *                                as a printable string
 235  *
 236  *   input: dhcp_reply_t *: contains the status structure to print
 237  *  output: const char *: newline terminated printable string
 238  */
 239 const char *
 240 dhcp_status_reply_to_string(dhcp_ipc_reply_t *reply)
 241 {
 242         static char str[1024];
 243         size_t reply_size;
 244         dhcp_status_t *status;
 245 
 246         status = dhcp_ipc_get_data(reply, &reply_size, NULL);
 247         if (reply_size < DHCP_STATUS_VER1_SIZE)
 248                 return ("<Internal error: status msg size>\n");
 249 
 250         (void) snprintf(str, sizeof (str), DHCP_STATUS_STR,
 251             status->if_name, dhcp_state_to_string(status->if_state),
 252             status->if_sent, status->if_recv, status->if_bad_offers);
 253 
 254         if (status->if_dflags & DHCP_IF_PRIMARY)
 255                 (void) strlcat(str, "[PRIMARY] ", sizeof (str));
 256 
 257         if (status->if_dflags & DHCP_IF_BOOTP)
 258                 (void) strlcat(str, "[BOOTP] ", sizeof (str));
 259 
 260         if (status->if_dflags & DHCP_IF_FAILED)
 261                 (void) strlcat(str, "[FAILED] ", sizeof (str));
 262 
 263         if (status->if_dflags & DHCP_IF_BUSY)
 264                 (void) strlcat(str, "[BUSY] ", sizeof (str));
 265 
 266         if (status->if_dflags & DHCP_IF_V6)
 267                 (void) strlcat(str, "[V6] ", sizeof (str));
 268 
 269         (void) strlcat(str, "\n", sizeof (str));
 270 
 271         switch (status->if_state) {
 272         case BOUND:
 273         case RENEWING:
 274         case REBINDING:
 275                 break;
 276         default:
 277                 return (str);
 278         }
 279 
 280         (void) strlcat(str, "(Began, Expires, Renew) = (", sizeof (str));
 281         (void) strlcat(str, time_to_string(status->if_began), sizeof (str));
 282         (void) strlcat(str, ", ", sizeof (str));
 283         (void) strlcat(str, time_to_string(status->if_lease), sizeof (str));
 284         (void) strlcat(str, ", ", sizeof (str));
 285         (void) strlcat(str, time_to_string(status->if_t1), sizeof (str));
 286         (void) strlcat(str, ")\n", sizeof (str));
 287         return (str);
 288 }