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 *
dhcp_state_to_string(DHCPSTATE state)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
init_template(void)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
dhcp_start_agent(int timeout)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 *
dhcp_status_hdr_string(void)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 *
time_to_string(time_t abs_time)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 *
dhcp_status_reply_to_string(dhcp_ipc_reply_t * reply)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