xref: /titanic_52/usr/src/cmd/cmd-inet/usr.lib/inetd/util.c (revision 24da5b34f49324ed742a340010ed5bd3d4e06625)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * General utility routines.
31  */
32 
33 #include <syslog.h>
34 #include <stdlib.h>
35 #include <stdio.h>
36 #include <stdarg.h>
37 #include <strings.h>
38 #include <time.h>
39 #include <errno.h>
40 #include <libintl.h>
41 #include <unistd.h>
42 #include "inetd_impl.h"
43 
44 
45 /* location of inetd's debug log file */
46 #define	DEBUG_LOG_FILE		"/var/tmp/inetd.log"
47 
48 /* size of buffer used in msg() to expand printf() like messages into */
49 #define	MSG_BUF_SIZE		1024
50 
51 /* size of buffer used in msg() to store a date/time string */
52 #define	TIME_BUF_SIZE		50
53 
54 /* number of pollfd we grow the pollfd array by at a time in set_pollfd() */
55 #define	POLLFDS_GROWTH_SIZE	16
56 
57 /* enumeration of message types supported by msg() */
58 typedef enum {
59 	MT_ERROR,
60 	MT_DEBUG,
61 	MT_WARN
62 } si_msg_type_t;
63 
64 /*
65  * Collection of information for each method type.
66  * NOTE:  This table is indexed into using the instance_method_t
67  * enumeration, so the ordering needs to be kept in synch.
68  */
69 method_type_info_t methods[] = {
70 	{IM_START, START_METHOD_NAME, IIS_NONE},
71 	{IM_ONLINE, ONLINE_METHOD_NAME, IIS_ONLINE},
72 	{IM_OFFLINE, OFFLINE_METHOD_NAME, IIS_OFFLINE},
73 	{IM_DISABLE, DISABLE_METHOD_NAME, IIS_DISABLED},
74 	{IM_REFRESH, REFRESH_METHOD_NAME, IIS_ONLINE},
75 	{IM_NONE, "none", IIS_NONE}
76 };
77 
78 struct pollfd	*poll_fds = NULL;
79 nfds_t		num_pollfds;
80 static FILE	*debug_fp = NULL;
81 
82 boolean_t	logging_enabled;
83 
84 void
85 msg_init(void)
86 {
87 	openlog(SYSLOG_IDENT, LOG_PID|LOG_CONS, LOG_DAEMON);
88 
89 	/* Try once at startup to open the log file. */
90 	debug_fp = fopen(DEBUG_LOG_FILE, "r+");
91 	if (debug_fp != NULL)
92 		(void) fseeko(debug_fp, 0, SEEK_END);
93 	logging_enabled = B_TRUE;
94 }
95 
96 void
97 msg_fini(void)
98 {
99 	logging_enabled = B_FALSE;
100 	if (debug_fp != NULL) {
101 		(void) fclose(debug_fp);
102 		debug_fp = NULL;
103 	}
104 	closelog();
105 }
106 
107 /*
108  * Outputs a msg. If 'type' is set tp MT_ERROR or MT_WARN the message goes
109  * to syslog with severitys LOG_ERROR and LOG_WARN respectively. For all
110  * values of 'type' the message is written to the debug log file, if it
111  * was openable when inetd started.
112  */
113 static void
114 msg(si_msg_type_t type, const char *format, va_list ap)
115 {
116 	/*
117 	 * Use a stack buffer so we stand more chance of reporting a
118 	 * memory shortage failure.
119 	 */
120 	char		buf[MSG_BUF_SIZE];
121 	char		timebuf[TIME_BUF_SIZE];
122 
123 	if (!logging_enabled)
124 		return;
125 
126 	(void) vsnprintf(buf, sizeof (buf), format, ap);
127 
128 	/*
129 	 * Log error and warning messages to syslog with appropriate severity.
130 	 */
131 	if (type == MT_ERROR) {
132 		syslog(LOG_ERR, "%s", buf);
133 	} else if (type == MT_WARN) {
134 		syslog(LOG_WARNING, "%s", buf);
135 	}
136 
137 	if (debug_fp != NULL) {
138 		struct tm	tms;
139 		time_t		tm;
140 
141 		/*
142 		 * We managed to open the log file at startup. Log all
143 		 * message types there - in addition to syslog.
144 		 */
145 		tm = time(NULL);
146 		(void) strftime(timebuf, sizeof (timebuf), NULL,
147 		    localtime_r(&tm, &tms));
148 		(void) fputs(timebuf, debug_fp);
149 		(void) fputs(": ", debug_fp);
150 
151 		if (type == MT_ERROR) {
152 			(void) fputs("ERROR: ", debug_fp);
153 		} else if (type == MT_DEBUG) {
154 			(void) fputs("DEBUG: ", debug_fp);
155 		} else if (type == MT_WARN) {
156 			(void) fputs("WARN: ", debug_fp);
157 		}
158 
159 		(void) fputs(buf, debug_fp);
160 
161 		if (buf[strlen(buf) - 1] != '\n')
162 			(void) fputc('\n', debug_fp);
163 
164 		(void) fflush(debug_fp);
165 	}
166 }
167 
168 /*
169  * Output a warning message. Unlike error_msg(), syslog doesn't get told
170  * to log to the console if syslogd isn't around.
171  */
172 void
173 warn_msg(const char *format, ...)
174 {
175 	va_list ap;
176 
177 	closelog();
178 	openlog(SYSLOG_IDENT, LOG_PID, LOG_DAEMON);
179 
180 	va_start(ap, format);
181 	msg(MT_WARN, format, ap);
182 	va_end(ap);
183 
184 	closelog();
185 	openlog(SYSLOG_IDENT, LOG_PID|LOG_CONS, LOG_DAEMON);
186 }
187 
188 void
189 debug_msg(const char *format, ...)
190 {
191 	va_list ap;
192 
193 	va_start(ap, format);
194 	msg(MT_DEBUG, format, ap);
195 	va_end(ap);
196 }
197 
198 void
199 error_msg(const char *format, ...)
200 {
201 	va_list ap;
202 
203 	va_start(ap, format);
204 	msg(MT_ERROR, format, ap);
205 	va_end(ap);
206 }
207 
208 void
209 poll_fini(void)
210 {
211 	if (poll_fds != NULL) {
212 		free(poll_fds);
213 		poll_fds = NULL;
214 	}
215 }
216 
217 struct pollfd *
218 find_pollfd(int fd)
219 {
220 	nfds_t n;
221 
222 	for (n = 0; n < num_pollfds; n++) {
223 		if (poll_fds[n].fd == fd)
224 			return (&(poll_fds[n]));
225 	}
226 	return (NULL);
227 }
228 
229 int
230 set_pollfd(int fd, uint16_t events)
231 {
232 	struct pollfd	*p;
233 	int		i;
234 
235 	debug_msg("Entering set_pollfd, fd: %d, num_pollfds: %d, fds: %x,"
236 	    " events: %hu", fd, num_pollfds, poll_fds, events);
237 
238 	p = find_pollfd(fd);
239 	if ((p == NULL) && ((p = find_pollfd(-1)) == NULL)) {
240 		if ((p = realloc(poll_fds,
241 		    ((num_pollfds + POLLFDS_GROWTH_SIZE) *
242 		    sizeof (struct pollfd)))) == NULL) {
243 			return (-1);
244 		}
245 		poll_fds = p;
246 
247 		for (i = 1; i < POLLFDS_GROWTH_SIZE; i++)
248 			poll_fds[num_pollfds + i].fd = -1;
249 
250 		p = &poll_fds[num_pollfds];
251 		num_pollfds += POLLFDS_GROWTH_SIZE;
252 	}
253 
254 	p->fd = fd;
255 	p->events = events;
256 	p->revents = 0;
257 
258 	return (0);
259 }
260 
261 void
262 clear_pollfd(int fd)
263 {
264 	struct pollfd *p;
265 
266 	if ((p = find_pollfd(fd)) != NULL) {
267 		p->fd = -1;
268 		p->events = 0;
269 		p->revents = 0;
270 	}
271 }
272 
273 boolean_t
274 isset_pollfd(int fd)
275 {
276 	struct pollfd *p = find_pollfd(fd);
277 
278 	return ((p != NULL) && (p->revents & POLLIN));
279 }
280 
281 /*
282  * An extension of read() that keeps retrying until either the full request has
283  * completed, the other end of the connection/pipe is closed, no data is
284  * readable for a non-blocking socket/pipe, or an unexpected error occurs.
285  * Returns 0 if the data is successfully read, 1 if the other end of the pipe/
286  * socket is closed or there's nothing to read from a non-blocking socket/pipe,
287  * else -1 if an unexpected error occurs.
288  */
289 int
290 safe_read(int fd, void *buf, size_t sz)
291 {
292 	int	ret;
293 	size_t  cnt = 0;
294 	char    *cp = (char *)buf;
295 
296 	if (sz == 0)
297 		return (0);
298 
299 	do {
300 		switch (ret = read(fd, cp + cnt, sz - cnt)) {
301 		case 0:			/* other end of pipe/socket closed */
302 			return (1);
303 		case -1:
304 			if (errno == EAGAIN) {		/* nothing to read */
305 				return (1);
306 			} else if (errno != EINTR) {
307 				error_msg(gettext("Unexpected read error: %s"),
308 				    strerror(errno));
309 				return (-1);
310 			}
311 			break;
312 
313 		default:
314 			cnt += ret;
315 		}
316 	} while (cnt != sz);
317 
318 	return (0);
319 }
320 
321 /*
322  * Return B_TRUE if instance 'inst' has exceeded its configured maximum
323  * concurrent copies limit, else B_FALSE.
324  */
325 boolean_t
326 copies_limit_exceeded(instance_t *inst)
327 {
328 	/* any value <=0 means that copies limits are disabled */
329 	return ((inst->config->basic->max_copies > 0) &&
330 	    (inst->copies >= inst->config->basic->max_copies));
331 }
332 
333 /*
334  * Cancel the method/con-rate offline timer associated with the instance.
335  */
336 void
337 cancel_inst_timer(instance_t *inst)
338 {
339 	(void) iu_cancel_timer(timer_queue, inst->timer_id, NULL);
340 	inst->timer_id = -1;
341 }
342 
343 /*
344  * Cancel the bind retry timer associated with the instance.
345  */
346 void
347 cancel_bind_timer(instance_t *inst)
348 {
349 	(void) iu_cancel_timer(timer_queue, inst->bind_timer_id, NULL);
350 	inst->bind_timer_id = -1;
351 }
352 
353 void
354 enable_blocking(int fd)
355 {
356 	int flags = fcntl(fd, F_GETFL, 0);
357 	(void) fcntl(fd, F_SETFL, (flags & ~O_NONBLOCK));
358 }
359 
360 void
361 disable_blocking(int fd)
362 {
363 	int flags = fcntl(fd, F_GETFL, 0);
364 	(void) fcntl(fd, F_SETFL, (flags | O_NONBLOCK));
365 }
366