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 2006 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <sys/types.h>
29 #include <sys/socket.h>
30 #include <sys/time.h>
31 #include <unistd.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include "dhcpagent_ipc.h"
36 #include "dhcpagent_util.h"
37 
38 /*
39  * Strings returned by dhcp_status_hdr_string() and
40  * dhcp_status_reply_to_string(). The first define is the header line, and
41  * the second defines line printed underneath.
42  * The spacing of fields must match.
43  */
44 #define	DHCP_STATUS_HDR	"Interface  State         Sent  Recv  Declined  Flags\n"
45 #define	DHCP_STATUS_STR	"%-10s %-12s %5d %5d %9d  "
46 
47 static const char *time_to_string(time_t abs_time);
48 
49 /*
50  * dhcp_state_to_string(): given a state, provides the state's name
51  *
52  *    input: DHCPSTATE: the state to get the name of
53  *   output: const char *: the state's name
54  */
55 
56 const char *
57 dhcp_state_to_string(DHCPSTATE state)
58 {
59 	const char *states[] = {
60 		"INIT",
61 		"SELECTING",
62 		"REQUESTING",
63 		"PRE_BOUND",
64 		"BOUND",
65 		"RENEWING",
66 		"REBINDING",
67 		"INFORMATION",
68 		"INIT_REBOOT",
69 		"ADOPTING",
70 		"INFORM_SENT"
71 	};
72 
73 	if (state < 0 || state >= DHCP_NSTATES)
74 		return ("<unknown>");
75 
76 	return (states[state]);
77 }
78 
79 /*
80  * dhcp_string_to_request(): maps a string into a request code
81  *
82  *    input: const char *: the string to map
83  *   output: dhcp_ipc_type_t: the request code, or -1 if unknown
84  */
85 
86 dhcp_ipc_type_t
87 dhcp_string_to_request(const char *request)
88 {
89 	static struct {
90 		const char	*string;
91 		dhcp_ipc_type_t  type;
92 	} types[] = {
93 		{ "drop",	DHCP_DROP	},
94 		{ "extend",	DHCP_EXTEND	},
95 		{ "inform",	DHCP_INFORM	},
96 		{ "ping",	DHCP_PING	},
97 		{ "release",	DHCP_RELEASE	},
98 		{ "start",	DHCP_START	},
99 		{ "status",	DHCP_STATUS	}
100 	};
101 
102 	unsigned int	i;
103 
104 	for (i = 0; i < (sizeof (types) / sizeof (*types)); i++)
105 		if (strcmp(types[i].string, request) == 0)
106 			return (types[i].type);
107 
108 	return (-1);
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 
126 	/*
127 	 * just send a dummy request to the agent to find out if it's
128 	 * up.  we do this instead of directly connecting to it since
129 	 * we want to make sure we follow its IPC conventions
130 	 * (otherwise, it will log warnings to syslog).
131 	 */
132 
133 	request = dhcp_ipc_alloc_request(DHCP_PING, "", NULL, 0,
134 	    DHCP_TYPE_NONE);
135 	if (request == NULL)
136 		return (-1);
137 
138 	error = dhcp_ipc_make_request(request, &reply, 0);
139 	if (error == 0) {
140 		free(reply);
141 		free(request);
142 		return (0);
143 	}
144 	if (error != DHCP_IPC_E_CONNECT) {
145 		free(request);
146 		return (-1);
147 	}
148 
149 	switch (fork()) {
150 
151 	case -1:
152 		free(request);
153 		return (-1);
154 
155 	case  0:
156 		(void) execl(DHCP_AGENT_PATH, DHCP_AGENT_PATH, (char *)0);
157 		_exit(EXIT_FAILURE);
158 
159 	default:
160 		break;
161 	}
162 
163 	while ((timeout != -1) && (time(NULL) - start_time < timeout)) {
164 		error = dhcp_ipc_make_request(request, &reply, 0);
165 		if (error == 0) {
166 			free(reply);
167 			free(request);
168 			return (0);
169 		} else if (error != DHCP_IPC_E_CONNECT)
170 			break;
171 		(void) sleep(1);
172 	}
173 
174 	free(request);
175 	return (-1);
176 }
177 
178 /*
179  * dhcp_status_hdr_string(): Return a string suitable to use as the header
180  *			     when printing DHCP_STATUS reply.
181  *  output: const char *: newline terminated printable string
182  */
183 const char *
184 dhcp_status_hdr_string(void)
185 {
186 	return (DHCP_STATUS_HDR);
187 }
188 
189 /*
190  * time_to_string(): Utility routine for printing time
191  *
192  *   input: time_t *: time_t to stringify
193  *  output: const char *: printable time
194  */
195 static const char *
196 time_to_string(time_t abs_time)
197 {
198 	static char time_buf[24];
199 	time_t tm = abs_time;
200 
201 	if (tm == DHCP_PERM)
202 		return ("Never");
203 
204 	if (strftime(time_buf, sizeof (time_buf), "%m/%d/%Y %R",
205 	    localtime(&tm)) == 0)
206 		return ("<unknown>");
207 
208 	return (time_buf);
209 }
210 
211 /*
212  * dhcp_status_reply_to_string(): Return DHCP IPC reply of type DHCP_STATUS
213  *				  as a printable string
214  *
215  *   input: dhcp_reply_t *: contains the status structure to print
216  *  output: const char *: newline terminated printable string
217  */
218 const char *
219 dhcp_status_reply_to_string(dhcp_ipc_reply_t *reply)
220 {
221 	static char str[1024];
222 	size_t reply_size;
223 	dhcp_status_t *status;
224 
225 	status = dhcp_ipc_get_data(reply, &reply_size, NULL);
226 	if (reply_size < DHCP_STATUS_VER1_SIZE)
227 		return ("<Internal error: status msg size>\n");
228 
229 	(void) snprintf(str, sizeof (str), DHCP_STATUS_STR,
230 	    status->if_name, dhcp_state_to_string(status->if_state),
231 	    status->if_sent, status->if_recv, status->if_bad_offers);
232 
233 	if (status->if_dflags & DHCP_IF_PRIMARY)
234 		(void) strlcat(str, "[PRIMARY] ", sizeof (str));
235 
236 	if (status->if_dflags & DHCP_IF_BOOTP)
237 		(void) strlcat(str, "[BOOTP] ", sizeof (str));
238 
239 	if (status->if_dflags & DHCP_IF_FAILED)
240 		(void) strlcat(str, "[FAILED] ", sizeof (str));
241 
242 	if (status->if_dflags & DHCP_IF_BUSY)
243 		(void) strlcat(str, "[BUSY] ", sizeof (str));
244 
245 	(void) strlcat(str, "\n", sizeof (str));
246 
247 	switch (status->if_state) {
248 	case BOUND:
249 	case RENEWING:
250 	case REBINDING:
251 		break;
252 	default:
253 		return (str);
254 	}
255 
256 	(void) strlcat(str, "(Began, Expires, Renew) = (", sizeof (str));
257 	(void) strlcat(str, time_to_string(status->if_began), sizeof (str));
258 	(void) strlcat(str, ", ", sizeof (str));
259 	(void) strlcat(str, time_to_string(status->if_lease), sizeof (str));
260 	(void) strlcat(str, ", ", sizeof (str));
261 	(void) strlcat(str, time_to_string(status->if_t1), sizeof (str));
262 	(void) strlcat(str, ")\n", sizeof (str));
263 	return (str);
264 }
265