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