1262266Sbapt/*
2289123Sbapt * Copyright (c) 2008-2014, Simon Schubert <2@0x2c.org>.
3262266Sbapt * Copyright (c) 2008 The DragonFly Project.  All rights reserved.
4262266Sbapt *
5262266Sbapt * This code is derived from software contributed to The DragonFly Project
6289123Sbapt * by Simon Schubert <2@0x2c.org>.
7262266Sbapt *
8262266Sbapt * Redistribution and use in source and binary forms, with or without
9262266Sbapt * modification, are permitted provided that the following conditions
10262266Sbapt * are met:
11262266Sbapt *
12262266Sbapt * 1. Redistributions of source code must retain the above copyright
13262266Sbapt *    notice, this list of conditions and the following disclaimer.
14262266Sbapt * 2. Redistributions in binary form must reproduce the above copyright
15262266Sbapt *    notice, this list of conditions and the following disclaimer in
16262266Sbapt *    the documentation and/or other materials provided with the
17262266Sbapt *    distribution.
18262266Sbapt * 3. Neither the name of The DragonFly Project nor the names of its
19262266Sbapt *    contributors may be used to endorse or promote products derived
20262266Sbapt *    from this software without specific, prior written permission.
21262266Sbapt *
22262266Sbapt * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23262266Sbapt * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24262266Sbapt * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25262266Sbapt * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
26262266Sbapt * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27262266Sbapt * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
28262266Sbapt * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29262266Sbapt * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
30262266Sbapt * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
31262266Sbapt * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
32262266Sbapt * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33262266Sbapt * SUCH DAMAGE.
34262266Sbapt */
35262266Sbapt
36262266Sbapt#include <sys/param.h>
37262266Sbapt#include <sys/file.h>
38262266Sbapt
39262266Sbapt#include <ctype.h>
40262266Sbapt#include <errno.h>
41262266Sbapt#include <fcntl.h>
42262266Sbapt#include <netdb.h>
43262266Sbapt#include <pwd.h>
44262266Sbapt#include <setjmp.h>
45262266Sbapt#include <signal.h>
46262266Sbapt#include <stdio.h>
47262266Sbapt#include <syslog.h>
48262266Sbapt#include <unistd.h>
49262266Sbapt
50262266Sbapt#include "dma.h"
51262266Sbapt
52262266Sbaptconst char *
53262266Sbapthostname(void)
54262266Sbapt{
55262266Sbapt#ifndef HOST_NAME_MAX
56262266Sbapt#define HOST_NAME_MAX	255
57262266Sbapt#endif
58262266Sbapt	static char name[HOST_NAME_MAX+1];
59262266Sbapt	static int initialized = 0;
60262266Sbapt	char *s;
61262266Sbapt
62262266Sbapt	if (initialized)
63262266Sbapt		return (name);
64262266Sbapt
65262266Sbapt	if (config.mailname == NULL || !*config.mailname)
66262266Sbapt		goto local;
67262266Sbapt
68262266Sbapt	if (config.mailname[0] == '/') {
69262266Sbapt		/*
70262266Sbapt		 * If the mailname looks like an absolute path,
71262266Sbapt		 * treat it as a file.
72262266Sbapt		 */
73262266Sbapt		FILE *fp;
74262266Sbapt
75262266Sbapt		fp = fopen(config.mailname, "r");
76262266Sbapt		if (fp == NULL)
77262266Sbapt			goto local;
78262266Sbapt
79262266Sbapt		s = fgets(name, sizeof(name), fp);
80262266Sbapt		fclose(fp);
81262266Sbapt		if (s == NULL)
82262266Sbapt			goto local;
83262266Sbapt
84262266Sbapt		for (s = name; *s != 0 && (isalnum(*s) || strchr("_.-", *s)); ++s)
85262266Sbapt			/* NOTHING */;
86262266Sbapt		*s = 0;
87262266Sbapt
88262266Sbapt		if (!*name)
89262266Sbapt			goto local;
90262266Sbapt
91262266Sbapt		initialized = 1;
92262266Sbapt		return (name);
93262266Sbapt	} else {
94262266Sbapt		snprintf(name, sizeof(name), "%s", config.mailname);
95262266Sbapt		initialized = 1;
96262266Sbapt		return (name);
97262266Sbapt	}
98262266Sbapt
99262266Sbaptlocal:
100262266Sbapt	if (gethostname(name, sizeof(name)) != 0)
101262266Sbapt		*name = 0;
102262266Sbapt	/*
103262266Sbapt	 * gethostname() is allowed to truncate name without NUL-termination
104262266Sbapt	 * and at the same time not return an error.
105262266Sbapt	 */
106262266Sbapt	name[sizeof(name) - 1] = 0;
107262266Sbapt
108262266Sbapt	for (s = name; *s != 0 && (isalnum(*s) || strchr("_.-", *s)); ++s)
109262266Sbapt		/* NOTHING */;
110262266Sbapt	*s = 0;
111262266Sbapt
112262266Sbapt	if (!*name)
113262266Sbapt		snprintf(name, sizeof(name), "unknown-hostname");
114262266Sbapt
115262266Sbapt	initialized = 1;
116262266Sbapt	return (name);
117262266Sbapt}
118262266Sbapt
119262266Sbaptvoid
120262266Sbaptsetlogident(const char *fmt, ...)
121262266Sbapt{
122262266Sbapt	static char tag[50];
123262266Sbapt
124262266Sbapt	snprintf(tag, sizeof(tag), "%s", logident_base);
125262266Sbapt	if (fmt != NULL) {
126262266Sbapt		va_list ap;
127262266Sbapt		char sufx[50];
128262266Sbapt
129262266Sbapt		va_start(ap, fmt);
130262266Sbapt		vsnprintf(sufx, sizeof(sufx), fmt, ap);
131262266Sbapt		va_end(ap);
132262266Sbapt		snprintf(tag, sizeof(tag), "%s[%s]", logident_base, sufx);
133262266Sbapt	}
134262266Sbapt	closelog();
135262266Sbapt	openlog(tag, 0, LOG_MAIL);
136262266Sbapt}
137262266Sbapt
138262266Sbaptvoid
139262266Sbapterrlog(int exitcode, const char *fmt, ...)
140262266Sbapt{
141262266Sbapt	int oerrno = errno;
142262266Sbapt	va_list ap;
143262266Sbapt	char outs[ERRMSG_SIZE];
144262266Sbapt
145262266Sbapt	outs[0] = 0;
146262266Sbapt	if (fmt != NULL) {
147262266Sbapt		va_start(ap, fmt);
148262266Sbapt		vsnprintf(outs, sizeof(outs), fmt, ap);
149262266Sbapt		va_end(ap);
150262266Sbapt	}
151262266Sbapt
152262266Sbapt	errno = oerrno;
153262266Sbapt	if (*outs != 0) {
154262266Sbapt		syslog(LOG_ERR, "%s: %m", outs);
155262266Sbapt		fprintf(stderr, "%s: %s: %s\n", getprogname(), outs, strerror(oerrno));
156262266Sbapt	} else {
157262266Sbapt		syslog(LOG_ERR, "%m");
158262266Sbapt		fprintf(stderr, "%s: %s\n", getprogname(), strerror(oerrno));
159262266Sbapt	}
160262266Sbapt
161262266Sbapt	exit(exitcode);
162262266Sbapt}
163262266Sbapt
164262266Sbaptvoid
165262266Sbapterrlogx(int exitcode, const char *fmt, ...)
166262266Sbapt{
167262266Sbapt	va_list ap;
168262266Sbapt	char outs[ERRMSG_SIZE];
169262266Sbapt
170262266Sbapt	outs[0] = 0;
171262266Sbapt	if (fmt != NULL) {
172262266Sbapt		va_start(ap, fmt);
173262266Sbapt		vsnprintf(outs, sizeof(outs), fmt, ap);
174262266Sbapt		va_end(ap);
175262266Sbapt	}
176262266Sbapt
177262266Sbapt	if (*outs != 0) {
178262266Sbapt		syslog(LOG_ERR, "%s", outs);
179262266Sbapt		fprintf(stderr, "%s: %s\n", getprogname(), outs);
180262266Sbapt	} else {
181262266Sbapt		syslog(LOG_ERR, "Unknown error");
182262266Sbapt		fprintf(stderr, "%s: Unknown error\n", getprogname());
183262266Sbapt	}
184262266Sbapt
185262266Sbapt	exit(exitcode);
186262266Sbapt}
187262266Sbapt
188262266Sbaptstatic int
189262266Sbaptcheck_username(const char *name, uid_t ckuid)
190262266Sbapt{
191262266Sbapt	struct passwd *pwd;
192262266Sbapt
193262266Sbapt	if (name == NULL)
194262266Sbapt		return (0);
195262266Sbapt	pwd = getpwnam(name);
196262266Sbapt	if (pwd == NULL || pwd->pw_uid != ckuid)
197262266Sbapt		return (0);
198262266Sbapt	snprintf(username, sizeof(username), "%s", name);
199262266Sbapt	return (1);
200262266Sbapt}
201262266Sbapt
202262266Sbaptvoid
203262266Sbaptset_username(void)
204262266Sbapt{
205262266Sbapt	struct passwd *pwd;
206262266Sbapt
207262266Sbapt	useruid = getuid();
208262266Sbapt	if (check_username(getlogin(), useruid))
209262266Sbapt		return;
210262266Sbapt	if (check_username(getenv("LOGNAME"), useruid))
211262266Sbapt		return;
212262266Sbapt	if (check_username(getenv("USER"), useruid))
213262266Sbapt		return;
214262266Sbapt	pwd = getpwuid(useruid);
215262266Sbapt	if (pwd != NULL && pwd->pw_name != NULL && pwd->pw_name[0] != '\0') {
216262266Sbapt		if (check_username(pwd->pw_name, useruid))
217262266Sbapt			return;
218262266Sbapt	}
219262266Sbapt	snprintf(username, sizeof(username), "uid=%ld", (long)useruid);
220262266Sbapt}
221262266Sbapt
222262266Sbaptvoid
223262266Sbaptdeltmp(void)
224262266Sbapt{
225262266Sbapt	struct stritem *t;
226262266Sbapt
227262266Sbapt	SLIST_FOREACH(t, &tmpfs, next) {
228262266Sbapt		unlink(t->str);
229262266Sbapt	}
230262266Sbapt}
231262266Sbapt
232262266Sbaptstatic sigjmp_buf sigbuf;
233262266Sbaptstatic int sigbuf_valid;
234262266Sbapt
235262266Sbaptstatic void
236262266Sbaptsigalrm_handler(int signo)
237262266Sbapt{
238262266Sbapt	(void)signo;	/* so that gcc doesn't complain */
239262266Sbapt	if (sigbuf_valid)
240262266Sbapt		siglongjmp(sigbuf, 1);
241262266Sbapt}
242262266Sbapt
243262266Sbaptint
244262266Sbaptdo_timeout(int timeout, int dojmp)
245262266Sbapt{
246262266Sbapt	struct sigaction act;
247262266Sbapt	int ret = 0;
248262266Sbapt
249262266Sbapt	sigemptyset(&act.sa_mask);
250262266Sbapt	act.sa_flags = 0;
251262266Sbapt
252262266Sbapt	if (timeout) {
253262266Sbapt		act.sa_handler = sigalrm_handler;
254262266Sbapt		if (sigaction(SIGALRM, &act, NULL) != 0)
255262266Sbapt			syslog(LOG_WARNING, "can not set signal handler: %m");
256262266Sbapt		if (dojmp) {
257262266Sbapt			ret = sigsetjmp(sigbuf, 1);
258262266Sbapt			if (ret)
259262266Sbapt				goto disable;
260262266Sbapt			/* else just programmed */
261262266Sbapt			sigbuf_valid = 1;
262262266Sbapt		}
263262266Sbapt
264262266Sbapt		alarm(timeout);
265262266Sbapt	} else {
266262266Sbaptdisable:
267262266Sbapt		alarm(0);
268262266Sbapt
269262266Sbapt		act.sa_handler = SIG_IGN;
270262266Sbapt		if (sigaction(SIGALRM, &act, NULL) != 0)
271262266Sbapt			syslog(LOG_WARNING, "can not remove signal handler: %m");
272262266Sbapt		sigbuf_valid = 0;
273262266Sbapt	}
274262266Sbapt
275262266Sbapt	return (ret);
276262266Sbapt}
277262266Sbapt
278262266Sbaptint
279262266Sbaptopen_locked(const char *fname, int flags, ...)
280262266Sbapt{
281262266Sbapt	int mode = 0;
282262266Sbapt
283262266Sbapt	if (flags & O_CREAT) {
284262266Sbapt		va_list ap;
285262266Sbapt		va_start(ap, flags);
286262266Sbapt		mode = va_arg(ap, int);
287262266Sbapt		va_end(ap);
288262266Sbapt	}
289262266Sbapt
290262266Sbapt#ifndef O_EXLOCK
291262266Sbapt	int fd, save_errno;
292262266Sbapt
293262266Sbapt	fd = open(fname, flags, mode);
294262266Sbapt	if (fd < 0)
295262266Sbapt		return(fd);
296262266Sbapt	if (flock(fd, LOCK_EX|((flags & O_NONBLOCK)? LOCK_NB: 0)) < 0) {
297262266Sbapt		save_errno = errno;
298262266Sbapt		close(fd);
299262266Sbapt		errno = save_errno;
300262266Sbapt		return(-1);
301262266Sbapt	}
302262266Sbapt	return(fd);
303262266Sbapt#else
304262266Sbapt	return(open(fname, flags|O_EXLOCK, mode));
305262266Sbapt#endif
306262266Sbapt}
307262266Sbapt
308262266Sbaptchar *
309262266Sbaptrfc822date(void)
310262266Sbapt{
311262266Sbapt	static char str[50];
312262266Sbapt	size_t error;
313262266Sbapt	time_t now;
314262266Sbapt
315262266Sbapt	now = time(NULL);
316262266Sbapt	error = strftime(str, sizeof(str), "%a, %d %b %Y %T %z",
317262266Sbapt		       localtime(&now));
318262266Sbapt	if (error == 0)
319262266Sbapt		strcpy(str, "(date fail)");
320262266Sbapt	return (str);
321262266Sbapt}
322262266Sbapt
323262266Sbaptint
324262266Sbaptstrprefixcmp(const char *str, const char *prefix)
325262266Sbapt{
326262266Sbapt	return (strncasecmp(str, prefix, strlen(prefix)));
327262266Sbapt}
328262266Sbapt
329262266Sbaptvoid
330262266Sbaptinit_random(void)
331262266Sbapt{
332262266Sbapt	unsigned int seed;
333262266Sbapt	int rf;
334262266Sbapt
335262266Sbapt	rf = open("/dev/urandom", O_RDONLY);
336262266Sbapt	if (rf == -1)
337262266Sbapt		rf = open("/dev/random", O_RDONLY);
338262266Sbapt
339262266Sbapt	if (!(rf != -1 && read(rf, &seed, sizeof(seed)) == sizeof(seed)))
340262266Sbapt		seed = (time(NULL) ^ getpid()) + (uintptr_t)&seed;
341262266Sbapt
342262266Sbapt	srandom(seed);
343262266Sbapt
344262266Sbapt	if (rf != -1)
345262266Sbapt		close(rf);
346262266Sbapt}
347