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() */
51typedef 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 */
62method_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
71struct pollfd	*poll_fds = NULL;
72nfds_t		num_pollfds;
73
74boolean_t	syslog_open = B_FALSE;
75boolean_t	debug_enabled = B_FALSE;
76
77void
78msg_init(void)
79{
80	openlog(SYSLOG_IDENT, LOG_PID|LOG_CONS, LOG_DAEMON);
81	syslog_open = B_TRUE;
82}
83
84void
85msg_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 */
97static void
98msg(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 */
127void
128warn_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
143void
144debug_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
153void
154error_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
163void
164poll_fini(void)
165{
166	if (poll_fds != NULL) {
167		free(poll_fds);
168		poll_fds = NULL;
169	}
170}
171
172struct pollfd *
173find_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
184int
185set_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
213void
214clear_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
225boolean_t
226isset_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 */
241int
242safe_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 */
277boolean_t
278copies_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 */
288void
289cancel_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 */
298void
299cancel_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
305void
306enable_blocking(int fd)
307{
308	int flags = fcntl(fd, F_GETFL, 0);
309	(void) fcntl(fd, F_SETFL, (flags & ~O_NONBLOCK));
310}
311
312void
313disable_blocking(int fd)
314{
315	int flags = fcntl(fd, F_GETFL, 0);
316	(void) fcntl(fd, F_SETFL, (flags | O_NONBLOCK));
317}
318