1/*
2 * Copyright (c) 2008-2014, Simon Schubert <2@0x2c.org>.
3 * Copyright (c) 2008 The DragonFly Project.  All rights reserved.
4 *
5 * This code is derived from software contributed to The DragonFly Project
6 * by Simon Schubert <2@0x2c.org>.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in
16 *    the documentation and/or other materials provided with the
17 *    distribution.
18 * 3. Neither the name of The DragonFly Project nor the names of its
19 *    contributors may be used to endorse or promote products derived
20 *    from this software without specific, prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
26 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
28 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
30 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
31 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
32 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36#include <sys/param.h>
37#include <sys/file.h>
38
39#include <ctype.h>
40#include <errno.h>
41#include <fcntl.h>
42#include <netdb.h>
43#include <pwd.h>
44#include <setjmp.h>
45#include <signal.h>
46#include <stdio.h>
47#include <syslog.h>
48#include <unistd.h>
49
50#include "dma.h"
51
52const char *
53hostname(void)
54{
55#ifndef HOST_NAME_MAX
56#define HOST_NAME_MAX	255
57#endif
58	static char name[HOST_NAME_MAX+1];
59	static int initialized = 0;
60	char *s;
61
62	if (initialized)
63		return (name);
64
65	if (config.mailname == NULL || !*config.mailname)
66		goto local;
67
68	if (config.mailname[0] == '/') {
69		/*
70		 * If the mailname looks like an absolute path,
71		 * treat it as a file.
72		 */
73		FILE *fp;
74
75		fp = fopen(config.mailname, "r");
76		if (fp == NULL)
77			goto local;
78
79		s = fgets(name, sizeof(name), fp);
80		fclose(fp);
81		if (s == NULL)
82			goto local;
83
84		for (s = name; *s != 0 && (isalnum(*s) || strchr("_.-", *s)); ++s)
85			/* NOTHING */;
86		*s = 0;
87
88		if (!*name)
89			goto local;
90
91		initialized = 1;
92		return (name);
93	} else {
94		snprintf(name, sizeof(name), "%s", config.mailname);
95		initialized = 1;
96		return (name);
97	}
98
99local:
100	if (gethostname(name, sizeof(name)) != 0)
101		*name = 0;
102	/*
103	 * gethostname() is allowed to truncate name without NUL-termination
104	 * and at the same time not return an error.
105	 */
106	name[sizeof(name) - 1] = 0;
107
108	for (s = name; *s != 0 && (isalnum(*s) || strchr("_.-", *s)); ++s)
109		/* NOTHING */;
110	*s = 0;
111
112	if (!*name)
113		snprintf(name, sizeof(name), "unknown-hostname");
114
115	initialized = 1;
116	return (name);
117}
118
119void
120setlogident(const char *fmt, ...)
121{
122	static char tag[50];
123
124	snprintf(tag, sizeof(tag), "%s", logident_base);
125	if (fmt != NULL) {
126		va_list ap;
127		char sufx[50];
128
129		va_start(ap, fmt);
130		vsnprintf(sufx, sizeof(sufx), fmt, ap);
131		va_end(ap);
132		snprintf(tag, sizeof(tag), "%s[%s]", logident_base, sufx);
133	}
134	closelog();
135	openlog(tag, 0, LOG_MAIL);
136}
137
138void
139errlog(int exitcode, const char *fmt, ...)
140{
141	int oerrno = errno;
142	va_list ap;
143	char outs[ERRMSG_SIZE];
144
145	outs[0] = 0;
146	if (fmt != NULL) {
147		va_start(ap, fmt);
148		vsnprintf(outs, sizeof(outs), fmt, ap);
149		va_end(ap);
150	}
151
152	errno = oerrno;
153	if (*outs != 0) {
154		syslog(LOG_ERR, "%s: %m", outs);
155		fprintf(stderr, "%s: %s: %s\n", getprogname(), outs, strerror(oerrno));
156	} else {
157		syslog(LOG_ERR, "%m");
158		fprintf(stderr, "%s: %s\n", getprogname(), strerror(oerrno));
159	}
160
161	exit(exitcode);
162}
163
164void
165errlogx(int exitcode, const char *fmt, ...)
166{
167	va_list ap;
168	char outs[ERRMSG_SIZE];
169
170	outs[0] = 0;
171	if (fmt != NULL) {
172		va_start(ap, fmt);
173		vsnprintf(outs, sizeof(outs), fmt, ap);
174		va_end(ap);
175	}
176
177	if (*outs != 0) {
178		syslog(LOG_ERR, "%s", outs);
179		fprintf(stderr, "%s: %s\n", getprogname(), outs);
180	} else {
181		syslog(LOG_ERR, "Unknown error");
182		fprintf(stderr, "%s: Unknown error\n", getprogname());
183	}
184
185	exit(exitcode);
186}
187
188static int
189check_username(const char *name, uid_t ckuid)
190{
191	struct passwd *pwd;
192
193	if (name == NULL)
194		return (0);
195	pwd = getpwnam(name);
196	if (pwd == NULL || pwd->pw_uid != ckuid)
197		return (0);
198	snprintf(username, sizeof(username), "%s", name);
199	return (1);
200}
201
202void
203set_username(void)
204{
205	struct passwd *pwd;
206
207	useruid = getuid();
208	if (check_username(getlogin(), useruid))
209		return;
210	if (check_username(getenv("LOGNAME"), useruid))
211		return;
212	if (check_username(getenv("USER"), useruid))
213		return;
214	pwd = getpwuid(useruid);
215	if (pwd != NULL && pwd->pw_name != NULL && pwd->pw_name[0] != '\0') {
216		if (check_username(pwd->pw_name, useruid))
217			return;
218	}
219	snprintf(username, sizeof(username), "uid=%ld", (long)useruid);
220}
221
222void
223deltmp(void)
224{
225	struct stritem *t;
226
227	SLIST_FOREACH(t, &tmpfs, next) {
228		unlink(t->str);
229	}
230}
231
232static sigjmp_buf sigbuf;
233static int sigbuf_valid;
234
235static void
236sigalrm_handler(int signo)
237{
238	(void)signo;	/* so that gcc doesn't complain */
239	if (sigbuf_valid)
240		siglongjmp(sigbuf, 1);
241}
242
243int
244do_timeout(int timeout, int dojmp)
245{
246	struct sigaction act;
247	int ret = 0;
248
249	sigemptyset(&act.sa_mask);
250	act.sa_flags = 0;
251
252	if (timeout) {
253		act.sa_handler = sigalrm_handler;
254		if (sigaction(SIGALRM, &act, NULL) != 0)
255			syslog(LOG_WARNING, "can not set signal handler: %m");
256		if (dojmp) {
257			ret = sigsetjmp(sigbuf, 1);
258			if (ret)
259				goto disable;
260			/* else just programmed */
261			sigbuf_valid = 1;
262		}
263
264		alarm(timeout);
265	} else {
266disable:
267		alarm(0);
268
269		act.sa_handler = SIG_IGN;
270		if (sigaction(SIGALRM, &act, NULL) != 0)
271			syslog(LOG_WARNING, "can not remove signal handler: %m");
272		sigbuf_valid = 0;
273	}
274
275	return (ret);
276}
277
278int
279open_locked(const char *fname, int flags, ...)
280{
281	int mode = 0;
282
283	if (flags & O_CREAT) {
284		va_list ap;
285		va_start(ap, flags);
286		mode = va_arg(ap, int);
287		va_end(ap);
288	}
289
290#ifndef O_EXLOCK
291	int fd, save_errno;
292
293	fd = open(fname, flags, mode);
294	if (fd < 0)
295		return(fd);
296	if (flock(fd, LOCK_EX|((flags & O_NONBLOCK)? LOCK_NB: 0)) < 0) {
297		save_errno = errno;
298		close(fd);
299		errno = save_errno;
300		return(-1);
301	}
302	return(fd);
303#else
304	return(open(fname, flags|O_EXLOCK, mode));
305#endif
306}
307
308char *
309rfc822date(void)
310{
311	static char str[50];
312	size_t error;
313	time_t now;
314
315	now = time(NULL);
316	error = strftime(str, sizeof(str), "%a, %d %b %Y %T %z",
317		       localtime(&now));
318	if (error == 0)
319		strcpy(str, "(date fail)");
320	return (str);
321}
322
323int
324strprefixcmp(const char *str, const char *prefix)
325{
326	return (strncasecmp(str, prefix, strlen(prefix)));
327}
328
329void
330init_random(void)
331{
332	unsigned int seed;
333	int rf;
334
335	rf = open("/dev/urandom", O_RDONLY);
336	if (rf == -1)
337		rf = open("/dev/random", O_RDONLY);
338
339	if (!(rf != -1 && read(rf, &seed, sizeof(seed)) == sizeof(seed)))
340		seed = (time(NULL) ^ getpid()) + (uintptr_t)&seed;
341
342	srandom(seed);
343
344	if (rf != -1)
345		close(rf);
346}
347