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 } 282