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 2008 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 /*
29  * General utility routines.
30  */
31 
32 #include <syslog.h>
33 #include <stdlib.h>
34 #include <stdio.h>
35 #include <stdarg.h>
36 #include <strings.h>
37 #include <time.h>
38 #include <errno.h>
39 #include <libintl.h>
40 #include <unistd.h>
41 #include "inetd_impl.h"
42 
43 
44 /* size of buffer used in msg() to expand printf() like messages into */
45 #define	MSG_BUF_SIZE		1024
46 
47 /* number of pollfd we grow the pollfd array by at a time in set_pollfd() */
48 #define	POLLFDS_GROWTH_SIZE	16
49 
50 /* enumeration of message types supported by msg() */
51 typedef enum {
52 	MT_ERROR,
53 	MT_DEBUG,
54 	MT_WARN
55 } si_msg_type_t;
56 
57 /*
58  * Collection of information for each method type.
59  * NOTE:  This table is indexed into using the instance_method_t
60  * enumeration, so the ordering needs to be kept in synch.
61  */
62 method_type_info_t methods[] = {
63 	{IM_START, START_METHOD_NAME, IIS_NONE},
64 	{IM_ONLINE, ONLINE_METHOD_NAME, IIS_ONLINE},
65 	{IM_OFFLINE, OFFLINE_METHOD_NAME, IIS_OFFLINE},
66 	{IM_DISABLE, DISABLE_METHOD_NAME, IIS_DISABLED},
67 	{IM_REFRESH, REFRESH_METHOD_NAME, IIS_ONLINE},
68 	{IM_NONE, "none", IIS_NONE}
69 };
70 
71 struct pollfd	*poll_fds = NULL;
72 nfds_t		num_pollfds;
73 
74 boolean_t	syslog_open = B_FALSE;
75 boolean_t	debug_enabled = B_FALSE;
76 
77 void
78 msg_init(void)
79 {
80 	openlog(SYSLOG_IDENT, LOG_PID|LOG_CONS, LOG_DAEMON);
81 	syslog_open = B_TRUE;
82 }
83 
84 void
85 msg_fini(void)
86 {
87 	syslog_open = B_FALSE;
88 	closelog();
89 }
90 
91 /*
92  * Outputs a msg. If 'type' is set tp MT_ERROR or MT_WARN the message goes
93  * to syslog with severitys LOG_ERROR and LOG_WARN respectively. For all
94  * values of 'type' the message is written to the debug log file, if it
95  * was openable when inetd started.
96  */
97 static void
98 msg(si_msg_type_t type, const char *format, va_list ap)
99 {
100 	/*
101 	 * Use a stack buffer so we stand more chance of reporting a
102 	 * memory shortage failure.
103 	 */
104 	char		buf[MSG_BUF_SIZE];
105 
106 	if (!syslog_open)
107 		return;
108 
109 	(void) vsnprintf(buf, sizeof (buf), format, ap);
110 
111 	/*
112 	 * Log error and warning messages to syslog with appropriate severity.
113 	 */
114 	if (type == MT_ERROR) {
115 		syslog(LOG_ERR, "%s", buf);
116 	} else if (type == MT_WARN) {
117 		syslog(LOG_WARNING, "%s", buf);
118 	} else if (debug_enabled && type == MT_DEBUG) {
119 		syslog(LOG_DEBUG, "%s", buf);
120 	}
121 }
122 
123 /*
124  * Output a warning message. Unlike error_msg(), syslog doesn't get told
125  * to log to the console if syslogd isn't around.
126  */
127 void
128 warn_msg(const char *format, ...)
129 {
130 	va_list ap;
131 
132 	closelog();
133 	openlog(SYSLOG_IDENT, LOG_PID, LOG_DAEMON);
134 
135 	va_start(ap, format);
136 	msg(MT_WARN, format, ap);
137 	va_end(ap);
138 
139 	closelog();
140 	openlog(SYSLOG_IDENT, LOG_PID|LOG_CONS, LOG_DAEMON);
141 }
142 
143 void
144 debug_msg(const char *format, ...)
145 {
146 	va_list ap;
147 
148 	va_start(ap, format);
149 	msg(MT_DEBUG, format, ap);
150 	va_end(ap);
151 }
152 
153 void
154 error_msg(const char *format, ...)
155 {
156 	va_list ap;
157 
158 	va_start(ap, format);
159 	msg(MT_ERROR, format, ap);
160 	va_end(ap);
161 }
162 
163 void
164 poll_fini(void)
165 {
166 	if (poll_fds != NULL) {
167 		free(poll_fds);
168 		poll_fds = NULL;
169 	}
170 }
171 
172 struct pollfd *
173 find_pollfd(int fd)
174 {
175 	nfds_t n;
176 
177 	for (n = 0; n < num_pollfds; n++) {
178 		if (poll_fds[n].fd == fd)
179 			return (&(poll_fds[n]));
180 	}
181 	return (NULL);
182 }
183 
184 int
185 set_pollfd(int fd, uint16_t events)
186 {
187 	struct pollfd	*p;
188 	int		i;
189 
190 	p = find_pollfd(fd);
191 	if ((p == NULL) && ((p = find_pollfd(-1)) == NULL)) {
192 		if ((p = realloc(poll_fds,
193 		    ((num_pollfds + POLLFDS_GROWTH_SIZE) *
194 		    sizeof (struct pollfd)))) == NULL) {
195 			return (-1);
196 		}
197 		poll_fds = p;
198 
199 		for (i = 1; i < POLLFDS_GROWTH_SIZE; i++)
200 			poll_fds[num_pollfds + i].fd = -1;
201 
202 		p = &poll_fds[num_pollfds];
203 		num_pollfds += POLLFDS_GROWTH_SIZE;
204 	}
205 
206 	p->fd = fd;
207 	p->events = events;
208 	p->revents = 0;
209 
210 	return (0);
211 }
212 
213 void
214 clear_pollfd(int fd)
215 {
216 	struct pollfd *p;
217 
218 	if ((p = find_pollfd(fd)) != NULL) {
219 		p->fd = -1;
220 		p->events = 0;
221 		p->revents = 0;
222 	}
223 }
224 
225 boolean_t
226 isset_pollfd(int fd)
227 {
228 	struct pollfd *p = find_pollfd(fd);
229 
230 	return ((p != NULL) && (p->revents & POLLIN));
231 }
232 
233 /*
234  * An extension of read() that keeps retrying until either the full request has
235  * completed, the other end of the connection/pipe is closed, no data is
236  * readable for a non-blocking socket/pipe, or an unexpected error occurs.
237  * Returns 0 if the data is successfully read, 1 if the other end of the pipe/
238  * socket is closed or there's nothing to read from a non-blocking socket/pipe,
239  * else -1 if an unexpected error occurs.
240  */
241 int
242 safe_read(int fd, void *buf, size_t sz)
243 {
244 	int	ret;
245 	size_t  cnt = 0;
246 	char    *cp = (char *)buf;
247 
248 	if (sz == 0)
249 		return (0);
250 
251 	do {
252 		switch (ret = read(fd, cp + cnt, sz - cnt)) {
253 		case 0:			/* other end of pipe/socket closed */
254 			return (1);
255 		case -1:
256 			if (errno == EAGAIN) {		/* nothing to read */
257 				return (1);
258 			} else if (errno != EINTR) {
259 				error_msg(gettext("Unexpected read error: %s"),
260 				    strerror(errno));
261 				return (-1);
262 			}
263 			break;
264 
265 		default:
266 			cnt += ret;
267 		}
268 	} while (cnt != sz);
269 
270 	return (0);
271 }
272 
273 /*
274  * Return B_TRUE if instance 'inst' has exceeded its configured maximum
275  * concurrent copies limit, else B_FALSE.
276  */
277 boolean_t
278 copies_limit_exceeded(instance_t *inst)
279 {
280 	/* any value <=0 means that copies limits are disabled */
281 	return ((inst->config->basic->max_copies > 0) &&
282 	    (inst->copies >= inst->config->basic->max_copies));
283 }
284 
285 /*
286  * Cancel the method/con-rate offline timer associated with the instance.
287  */
288 void
289 cancel_inst_timer(instance_t *inst)
290 {
291 	(void) iu_cancel_timer(timer_queue, inst->timer_id, NULL);
292 	inst->timer_id = -1;
293 }
294 
295 /*
296  * Cancel the bind retry timer associated with the instance.
297  */
298 void
299 cancel_bind_timer(instance_t *inst)
300 {
301 	(void) iu_cancel_timer(timer_queue, inst->bind_timer_id, NULL);
302 	inst->bind_timer_id = -1;
303 }
304 
305 void
306 enable_blocking(int fd)
307 {
308 	int flags = fcntl(fd, F_GETFL, 0);
309 	(void) fcntl(fd, F_SETFL, (flags & ~O_NONBLOCK));
310 }
311 
312 void
313 disable_blocking(int fd)
314 {
315 	int flags = fcntl(fd, F_GETFL, 0);
316 	(void) fcntl(fd, F_SETFL, (flags | O_NONBLOCK));
317 }
318