120253Sjoerg/*-
220302Sjoerg * Copyright (C) 1996
320302Sjoerg *	David L. Nugent.  All rights reserved.
420253Sjoerg *
520253Sjoerg * Redistribution and use in source and binary forms, with or without
620253Sjoerg * modification, are permitted provided that the following conditions
720253Sjoerg * are met:
820253Sjoerg * 1. Redistributions of source code must retain the above copyright
920302Sjoerg *    notice, this list of conditions and the following disclaimer.
1020253Sjoerg * 2. Redistributions in binary form must reproduce the above copyright
1120253Sjoerg *    notice, this list of conditions and the following disclaimer in the
1220253Sjoerg *    documentation and/or other materials provided with the distribution.
1320253Sjoerg *
1420302Sjoerg * THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
1520253Sjoerg * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1620253Sjoerg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1720302Sjoerg * ARE DISCLAIMED.  IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
1820253Sjoerg * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1920253Sjoerg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2020253Sjoerg * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2120253Sjoerg * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2220253Sjoerg * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2320253Sjoerg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2420253Sjoerg * SUCH DAMAGE.
2520253Sjoerg */
2620253Sjoerg
2730259Scharnier#ifndef lint
2830259Scharnierstatic const char rcsid[] =
2950479Speter  "$FreeBSD: releng/11.0/usr.sbin/pw/pw_log.c 300564 2016-05-24 05:02:24Z truckman $";
3030259Scharnier#endif /* not lint */
3130259Scharnier
32300564Struckman#include <ctype.h>
33300564Struckman#include <err.h>
3420253Sjoerg#include <fcntl.h>
35286201Sbapt#include <string.h>
36286201Sbapt#include <stdarg.h>
3720253Sjoerg
3820253Sjoerg#include "pw.h"
3920253Sjoerg
40300564Struckmanstatic FILE	*logfile = NULL;
4120253Sjoerg
4220253Sjoergvoid
4320253Sjoergpw_log(struct userconf * cnf, int mode, int which, char const * fmt,...)
4420253Sjoerg{
45300564Struckman	va_list		argp;
46300564Struckman	time_t		now;
47300564Struckman	const char	*cp, *name;
48300564Struckman	struct tm	*t;
49300564Struckman	int		fd, i, rlen;
50300564Struckman	char		nfmt[256], sname[32];
5120253Sjoerg
52300564Struckman	if (cnf->logfile == NULL || cnf->logfile[0] == '\0') {
53300564Struckman		return;
54300564Struckman	}
55300564Struckman
56300564Struckman	if (logfile == NULL) {
57300564Struckman		/* With umask==0 we need to control file access modes on create */
58300564Struckman		fd = open(cnf->logfile, O_WRONLY | O_CREAT | O_APPEND, 0600);
59300564Struckman		if (fd == -1) {
60300564Struckman			return;
6120253Sjoerg		}
62300564Struckman		logfile = fdopen(fd, "a");
63300564Struckman		if (logfile == NULL) {
64300564Struckman			return;
65300564Struckman		}
66300564Struckman	}
6720253Sjoerg
68300564Struckman	if ((name = getenv("LOGNAME")) == NULL &&
69300564Struckman	    (name = getenv("USER")) == NULL) {
70300564Struckman		strcpy(sname, "unknown");
71300564Struckman	} else {
72300564Struckman		/*
73300564Struckman		 * Since "name" will be embedded in a printf-like format,
74300564Struckman		 * we must sanitize it:
75300564Struckman		 *
76300564Struckman		 *    Limit its length so other information in the message
77300564Struckman		 *    is not truncated
78300564Struckman		 *
79300564Struckman		 *    Squeeze out embedded whitespace for the benefit of
80300564Struckman		 *    log file parsers
81300564Struckman		 *
82300564Struckman		 *    Escape embedded % characters with another %
83300564Struckman		 */
84300564Struckman		for (i = 0, cp = name;
85300564Struckman		    *cp != '\0' && i < (int)sizeof(sname) - 1; cp++) {
86300564Struckman			if (*cp == '%') {
87300564Struckman				if (i < (int)sizeof(sname) - 2) {
88300564Struckman					sname[i++] = '%';
89300564Struckman					sname[i++] = '%';
90300564Struckman				} else {
91300564Struckman					break;
92300564Struckman				}
93300564Struckman			} else if (!isspace(*cp)) {
94300564Struckman				sname[i++] = *cp;
95300564Struckman			} /* else do nothing */
9620253Sjoerg		}
97300564Struckman		if (i == 0) {
98300564Struckman			strcpy(sname, "unknown");
99300564Struckman		} else {
100300564Struckman			sname[i] = '\0';
101300564Struckman		}
10220253Sjoerg	}
103300564Struckman	now = time(NULL);
104300564Struckman	t = localtime(&now);
105300564Struckman	/* ISO 8601 International Standard Date format */
106300564Struckman	strftime(nfmt, sizeof nfmt, "%Y-%m-%d %T ", t);
107300564Struckman	rlen = sizeof(nfmt) - strlen(nfmt);
108300564Struckman	if (rlen <= 0 || snprintf(nfmt + strlen(nfmt), rlen,
109300564Struckman	    "[%s:%s%s] %s\n", sname, Which[which], Modes[mode],
110300564Struckman	    fmt) >= rlen) {
111300564Struckman		warnx("log format overflow, user name=%s", sname);
112300564Struckman	} else {
113300564Struckman		va_start(argp, fmt);
114300564Struckman		vfprintf(logfile, nfmt, argp);
115300564Struckman		va_end(argp);
116300564Struckman		fflush(logfile);
117300564Struckman	}
11820253Sjoerg}
119