Thu Aug 17 08:53:01 2017 UTC ()
- since we use log_it for cron_pclose() use log_it for cron_popen() too
  so we can see the reason it failed.
- add log_itx() that takes a format and use it.
- simplify with asprintf()


(christos)
diff -r1.8 -r1.9 src/external/bsd/cron/dist/do_command.c
diff -r1.4 -r1.5 src/external/bsd/cron/dist/funcs.h
diff -r1.4 -r1.5 src/external/bsd/cron/dist/misc.c

cvs diff -r1.8 -r1.9 src/external/bsd/cron/dist/do_command.c (switch to unified diff)

--- src/external/bsd/cron/dist/do_command.c 2017/06/09 17:36:30 1.8
+++ src/external/bsd/cron/dist/do_command.c 2017/08/17 08:53:00 1.9
@@ -1,602 +1,601 @@ @@ -1,602 +1,601 @@
1/* $NetBSD: do_command.c,v 1.8 2017/06/09 17:36:30 christos Exp $ */ 1/* $NetBSD: do_command.c,v 1.9 2017/08/17 08:53:00 christos Exp $ */
2 2
3/* Copyright 1988,1990,1993,1994 by Paul Vixie 3/* Copyright 1988,1990,1993,1994 by Paul Vixie
4 * All rights reserved 4 * All rights reserved
5 */ 5 */
6 6
7/* 7/*
8 * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") 8 * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
9 * Copyright (c) 1997,2000 by Internet Software Consortium, Inc. 9 * Copyright (c) 1997,2000 by Internet Software Consortium, Inc.
10 * 10 *
11 * Permission to use, copy, modify, and distribute this software for any 11 * Permission to use, copy, modify, and distribute this software for any
12 * purpose with or without fee is hereby granted, provided that the above 12 * purpose with or without fee is hereby granted, provided that the above
13 * copyright notice and this permission notice appear in all copies. 13 * copyright notice and this permission notice appear in all copies.
14 * 14 *
15 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES 15 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
16 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 16 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
17 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR 17 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
18 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 18 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 19 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
20 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 20 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
21 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 21 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22 */ 22 */
23#include <sys/cdefs.h> 23#include <sys/cdefs.h>
24#if !defined(lint) && !defined(LINT) 24#if !defined(lint) && !defined(LINT)
25#if 0 25#if 0
26static char rcsid[] = "Id: do_command.c,v 1.9 2004/01/23 18:56:42 vixie Exp"; 26static char rcsid[] = "Id: do_command.c,v 1.9 2004/01/23 18:56:42 vixie Exp";
27#else 27#else
28__RCSID("$NetBSD: do_command.c,v 1.8 2017/06/09 17:36:30 christos Exp $"); 28__RCSID("$NetBSD: do_command.c,v 1.9 2017/08/17 08:53:00 christos Exp $");
29#endif 29#endif
30#endif 30#endif
31 31
32#include "cron.h" 32#include "cron.h"
33#include <unistd.h> 33#include <unistd.h>
34 34
35static int child_process(entry *); 35static int child_process(entry *);
36static int safe_p(const char *, const char *); 36static int safe_p(const char *, const char *);
37 37
38void 38void
39do_command(entry *e, user *u) { 39do_command(entry *e, user *u) {
40 int retval; 40 int retval;
41 41
42 Debug(DPROC, ("[%ld] do_command(%s, (%s,%ld,%ld))\n", 42 Debug(DPROC, ("[%ld] do_command(%s, (%s,%ld,%ld))\n",
43 (long)getpid(), e->cmd, u->name, 43 (long)getpid(), e->cmd, u->name,
44 (long)e->pwd->pw_uid, (long)e->pwd->pw_gid)); 44 (long)e->pwd->pw_uid, (long)e->pwd->pw_gid));
45 45
46 /* fork to become asynchronous -- parent process is done immediately, 46 /* fork to become asynchronous -- parent process is done immediately,
47 * and continues to run the normal cron code, which means return to 47 * and continues to run the normal cron code, which means return to
48 * tick(). the child and grandchild don't leave this function, alive. 48 * tick(). the child and grandchild don't leave this function, alive.
49 * 49 *
50 * vfork() is unsuitable, since we have much to do, and the parent 50 * vfork() is unsuitable, since we have much to do, and the parent
51 * needs to be able to run off and fork other processes. 51 * needs to be able to run off and fork other processes.
52 */ 52 */
53 switch (fork()) { 53 switch (fork()) {
54 case -1: 54 case -1:
55 log_it("CRON", getpid(), "error", "can't fork"); 55 log_it("CRON", getpid(), "error", "can't fork");
56 break; 56 break;
57 case 0: 57 case 0:
58 /* child process */ 58 /* child process */
59 acquire_daemonlock(1); 59 acquire_daemonlock(1);
60 retval = child_process(e); 60 retval = child_process(e);
61 Debug(DPROC, ("[%ld] child process done (rc=%d), exiting\n", 61 Debug(DPROC, ("[%ld] child process done (rc=%d), exiting\n",
62 (long)getpid(), retval)); 62 (long)getpid(), retval));
63 _exit(retval); 63 _exit(retval);
64 break; 64 break;
65 default: 65 default:
66 /* parent process */ 66 /* parent process */
67 break; 67 break;
68 } 68 }
69 Debug(DPROC, ("[%ld] main process returning to work\n",(long)getpid())); 69 Debug(DPROC, ("[%ld] main process returning to work\n",(long)getpid()));
70} 70}
71 71
72static void 72static void
73sigchld_handler(int signo) { 73sigchld_handler(int signo) {
74 for (;;) { 74 for (;;) {
75 WAIT_T waiter; 75 WAIT_T waiter;
76 PID_T pid = waitpid(-1, &waiter, WNOHANG); 76 PID_T pid = waitpid(-1, &waiter, WNOHANG);
77 77
78 switch (pid) { 78 switch (pid) {
79 case -1: 79 case -1:
80 if (errno == EINTR) 80 if (errno == EINTR)
81 continue; 81 continue;
82 case 0: 82 case 0:
83 return; 83 return;
84 default: 84 default:
85 break; 85 break;
86 } 86 }
87 } 87 }
88} 88}
89 89
90extern char **environ; 90extern char **environ;
91static int 91static int
92child_process(entry *e) { 92child_process(entry *e) {
93 int stdin_pipe[2], stdout_pipe[2]; 93 int stdin_pipe[2], stdout_pipe[2];
94 char * volatile input_data; 94 char * volatile input_data;
95 char *homedir, *usernm, * volatile mailto; 95 char *homedir, *usernm, * volatile mailto;
96 struct sigaction sact; 96 struct sigaction sact;
97 char **envp = e->envp; 97 char **envp = e->envp;
98 int retval = OK_EXIT; 98 int retval = OK_EXIT;
99 99
100 Debug(DPROC, ("[%ld] child_process('%s')\n", (long)getpid(), e->cmd)); 100 Debug(DPROC, ("[%ld] child_process('%s')\n", (long)getpid(), e->cmd));
101 101
102 setproctitle("running job"); 102 setproctitle("running job");
103 103
104 /* discover some useful and important environment settings 104 /* discover some useful and important environment settings
105 */ 105 */
106 usernm = e->pwd->pw_name; 106 usernm = e->pwd->pw_name;
107 mailto = env_get("MAILTO", envp); 107 mailto = env_get("MAILTO", envp);
108 108
109 memset(&sact, 0, sizeof(sact)); 109 memset(&sact, 0, sizeof(sact));
110 sigemptyset(&sact.sa_mask); 110 sigemptyset(&sact.sa_mask);
111 sact.sa_flags = 0; 111 sact.sa_flags = 0;
112#ifdef SA_RESTART 112#ifdef SA_RESTART
113 sact.sa_flags |= SA_RESTART; 113 sact.sa_flags |= SA_RESTART;
114#endif 114#endif
115 sact.sa_handler = sigchld_handler; 115 sact.sa_handler = sigchld_handler;
116 (void) sigaction(SIGCHLD, &sact, NULL); 116 (void) sigaction(SIGCHLD, &sact, NULL);
117 117
118 /* create some pipes to talk to our future child 118 /* create some pipes to talk to our future child
119 */ 119 */
120 if (pipe(stdin_pipe) == -1) /* child's stdin */ 120 if (pipe(stdin_pipe) == -1) /* child's stdin */
121 log_it("CRON", getpid(), "error", "create child stdin pipe"); 121 log_it("CRON", getpid(), "error", "create child stdin pipe");
122 if (pipe(stdout_pipe) == -1) /* child's stdout */ 122 if (pipe(stdout_pipe) == -1) /* child's stdout */
123 log_it("CRON", getpid(), "error", "create child stdout pipe"); 123 log_it("CRON", getpid(), "error", "create child stdout pipe");
124  124
125 /* since we are a forked process, we can diddle the command string 125 /* since we are a forked process, we can diddle the command string
126 * we were passed -- nobody else is going to use it again, right? 126 * we were passed -- nobody else is going to use it again, right?
127 * 127 *
128 * if a % is present in the command, previous characters are the 128 * if a % is present in the command, previous characters are the
129 * command, and subsequent characters are the additional input to 129 * command, and subsequent characters are the additional input to
130 * the command. An escaped % will have the escape character stripped 130 * the command. An escaped % will have the escape character stripped
131 * from it. Subsequent %'s will be transformed into newlines, 131 * from it. Subsequent %'s will be transformed into newlines,
132 * but that happens later. 132 * but that happens later.
133 */ 133 */
134 /*local*/{ 134 /*local*/{
135 int escaped = FALSE; 135 int escaped = FALSE;
136 int ch; 136 int ch;
137 char *p; 137 char *p;
138 138
139 /* translation: 139 /* translation:
140 * \% -> % 140 * \% -> %
141 * % -> end of command, following is command input. 141 * % -> end of command, following is command input.
142 * \x -> \x for all x != % 142 * \x -> \x for all x != %
143 */ 143 */
144 input_data = p = e->cmd; 144 input_data = p = e->cmd;
145 while ((ch = *input_data++) != '\0') { 145 while ((ch = *input_data++) != '\0') {
146 if (escaped) { 146 if (escaped) {
147 if (ch != '%') 147 if (ch != '%')
148 *p++ = '\\'; 148 *p++ = '\\';
149 } else { 149 } else {
150 if (ch == '%') { 150 if (ch == '%') {
151 break; 151 break;
152 } 152 }
153 } 153 }
154 154
155 if (!(escaped = (ch == '\\'))) { 155 if (!(escaped = (ch == '\\'))) {
156 *p++ = (char)ch; 156 *p++ = (char)ch;
157 } 157 }
158 } 158 }
159 if (ch == '\0') { 159 if (ch == '\0') {
160 /* move pointer back, so that code below 160 /* move pointer back, so that code below
161 * won't think we encountered % sequence */ 161 * won't think we encountered % sequence */
162 input_data--; 162 input_data--;
163 } 163 }
164 if (escaped) 164 if (escaped)
165 *p++ = '\\'; 165 *p++ = '\\';
166 166
167 *p = '\0'; 167 *p = '\0';
168 } 168 }
169 169
170#ifdef USE_PAM 170#ifdef USE_PAM
171 if (!cron_pam_start(usernm)) 171 if (!cron_pam_start(usernm))
172 return ERROR_EXIT; 172 return ERROR_EXIT;
173 173
174 if (!(envp = cron_pam_getenvlist(envp))) { 174 if (!(envp = cron_pam_getenvlist(envp))) {
175 retval = ERROR_EXIT; 175 retval = ERROR_EXIT;
176 goto child_process_end; 176 goto child_process_end;
177 } 177 }
178#endif 178#endif
179 179
180 /* fork again, this time so we can exec the user's command. 180 /* fork again, this time so we can exec the user's command.
181 */ 181 */
182 switch (vfork()) { 182 switch (vfork()) {
183 case -1: 183 case -1:
184 retval = ERROR_EXIT; 184 retval = ERROR_EXIT;
185 goto child_process_end; 185 goto child_process_end;
186 /*NOTREACHED*/ 186 /*NOTREACHED*/
187 case 0: 187 case 0:
188 Debug(DPROC, ("[%ld] grandchild process vfork()'ed\n", 188 Debug(DPROC, ("[%ld] grandchild process vfork()'ed\n",
189 (long)getpid())); 189 (long)getpid()));
190 190
191 /* write a log message. we've waited this long to do it 191 /* write a log message. we've waited this long to do it
192 * because it was not until now that we knew the PID that 192 * because it was not until now that we knew the PID that
193 * the actual user command shell was going to get and the 193 * the actual user command shell was going to get and the
194 * PID is part of the log message. 194 * PID is part of the log message.
195 */ 195 */
196 if ((e->flags & DONT_LOG) == 0) { 196 if ((e->flags & DONT_LOG) == 0) {
197 char *x = mkprints(e->cmd, strlen(e->cmd)); 197 char *x = mkprints(e->cmd, strlen(e->cmd));
198 198
199 log_it(usernm, getpid(), "CMD START", x); 199 log_it(usernm, getpid(), "CMD START", x);
200 free(x); 200 free(x);
201 } 201 }
202 202
203 /* that's the last thing we'll log. close the log files. 203 /* that's the last thing we'll log. close the log files.
204 */ 204 */
205 log_close(); 205 log_close();
206 206
207 /* get new pgrp, void tty, etc. 207 /* get new pgrp, void tty, etc.
208 */ 208 */
209 if (setsid() == -1) 209 if (setsid() == -1)
210 syslog(LOG_ERR, "setsid() failure: %m"); 210 syslog(LOG_ERR, "setsid() failure: %m");
211 211
212 /* close the pipe ends that we won't use. this doesn't affect 212 /* close the pipe ends that we won't use. this doesn't affect
213 * the parent, who has to read and write them; it keeps the 213 * the parent, who has to read and write them; it keeps the
214 * kernel from recording us as a potential client TWICE -- 214 * kernel from recording us as a potential client TWICE --
215 * which would keep it from sending SIGPIPE in otherwise 215 * which would keep it from sending SIGPIPE in otherwise
216 * appropriate circumstances. 216 * appropriate circumstances.
217 */ 217 */
218 (void)close(stdin_pipe[WRITE_PIPE]); 218 (void)close(stdin_pipe[WRITE_PIPE]);
219 (void)close(stdout_pipe[READ_PIPE]); 219 (void)close(stdout_pipe[READ_PIPE]);
220 220
221 /* grandchild process. make std{in,out} be the ends of 221 /* grandchild process. make std{in,out} be the ends of
222 * pipes opened by our daddy; make stderr go to stdout. 222 * pipes opened by our daddy; make stderr go to stdout.
223 */ 223 */
224 if (stdin_pipe[READ_PIPE] != STDIN) { 224 if (stdin_pipe[READ_PIPE] != STDIN) {
225 (void)dup2(stdin_pipe[READ_PIPE], STDIN); 225 (void)dup2(stdin_pipe[READ_PIPE], STDIN);
226 (void)close(stdin_pipe[READ_PIPE]); 226 (void)close(stdin_pipe[READ_PIPE]);
227 } 227 }
228 if (stdout_pipe[WRITE_PIPE] != STDOUT) { 228 if (stdout_pipe[WRITE_PIPE] != STDOUT) {
229 (void)dup2(stdout_pipe[WRITE_PIPE], STDOUT); 229 (void)dup2(stdout_pipe[WRITE_PIPE], STDOUT);
230 (void)close(stdout_pipe[WRITE_PIPE]); 230 (void)close(stdout_pipe[WRITE_PIPE]);
231 } 231 }
232 (void)dup2(STDOUT, STDERR); 232 (void)dup2(STDOUT, STDERR);
233 233
234 /* set our directory, uid and gid. Set gid first, since once 234 /* set our directory, uid and gid. Set gid first, since once
235 * we set uid, we've lost root privledges. 235 * we set uid, we've lost root privledges.
236 */ 236 */
237#ifdef LOGIN_CAP 237#ifdef LOGIN_CAP
238 { 238 {
239#ifdef BSD_AUTH 239#ifdef BSD_AUTH
240 auth_session_t *as; 240 auth_session_t *as;
241#endif 241#endif
242 login_cap_t *lc; 242 login_cap_t *lc;
243 char *p; 243 char *p;
244 244
245 if ((lc = login_getclass(e->pwd->pw_class)) == NULL) { 245 if ((lc = login_getclass(e->pwd->pw_class)) == NULL) {
246 warnx("unable to get login class for `%s'", 246 warnx("unable to get login class for `%s'",
247 e->pwd->pw_name); 247 e->pwd->pw_name);
248 _exit(ERROR_EXIT); 248 _exit(ERROR_EXIT);
249 } 249 }
250 if (setusercontext(lc, e->pwd, e->pwd->pw_uid, LOGIN_SETALL) < 0) { 250 if (setusercontext(lc, e->pwd, e->pwd->pw_uid, LOGIN_SETALL) < 0) {
251 warnx("setusercontext failed for `%s'", 251 warnx("setusercontext failed for `%s'",
252 e->pwd->pw_name); 252 e->pwd->pw_name);
253 _exit(ERROR_EXIT); 253 _exit(ERROR_EXIT);
254 } 254 }
255#ifdef BSD_AUTH 255#ifdef BSD_AUTH
256 as = auth_open(); 256 as = auth_open();
257 if (as == NULL || auth_setpwd(as, e->pwd) != 0) { 257 if (as == NULL || auth_setpwd(as, e->pwd) != 0) {
258 warn("can't malloc"); 258 warn("can't malloc");
259 _exit(ERROR_EXIT); 259 _exit(ERROR_EXIT);
260 } 260 }
261 if (auth_approval(as, lc, usernm, "cron") <= 0) { 261 if (auth_approval(as, lc, usernm, "cron") <= 0) {
262 warnx("approval failed for `%s'", 262 warnx("approval failed for `%s'",
263 e->pwd->pw_name); 263 e->pwd->pw_name);
264 _exit(ERROR_EXIT); 264 _exit(ERROR_EXIT);
265 } 265 }
266 auth_close(as); 266 auth_close(as);
267#endif /* BSD_AUTH */ 267#endif /* BSD_AUTH */
268 login_close(lc); 268 login_close(lc);
269 269
270 /* If no PATH specified in crontab file but 270 /* If no PATH specified in crontab file but
271 * we just added one via login.conf, add it to 271 * we just added one via login.conf, add it to
272 * the crontab environment. 272 * the crontab environment.
273 */ 273 */
274 if (env_get("PATH", envp) == NULL && environ != NULL) { 274 if (env_get("PATH", envp) == NULL && environ != NULL) {
275 if ((p = getenv("PATH")) != NULL) 275 if ((p = getenv("PATH")) != NULL)
276 envp = env_set(envp, p); 276 envp = env_set(envp, p);
277 } 277 }
278 } 278 }
279#else 279#else
280 if (setgid(e->pwd->pw_gid) != 0) { 280 if (setgid(e->pwd->pw_gid) != 0) {
281 syslog(LOG_ERR, "setgid(%d) failed for %s: %m", 281 syslog(LOG_ERR, "setgid(%d) failed for %s: %m",
282 e->pwd->pw_gid, e->pwd->pw_name); 282 e->pwd->pw_gid, e->pwd->pw_name);
283 _exit(ERROR_EXIT); 283 _exit(ERROR_EXIT);
284 } 284 }
285 if (initgroups(usernm, e->pwd->pw_gid) != 0) { 285 if (initgroups(usernm, e->pwd->pw_gid) != 0) {
286 syslog(LOG_ERR, "initgroups(%s, %d) failed for %s: %m", 286 syslog(LOG_ERR, "initgroups(%s, %d) failed for %s: %m",
287 usernm, e->pwd->pw_gid, e->pwd->pw_name); 287 usernm, e->pwd->pw_gid, e->pwd->pw_name);
288 _exit(ERROR_EXIT); 288 _exit(ERROR_EXIT);
289 } 289 }
290#if (defined(BSD)) && (BSD >= 199103) 290#if (defined(BSD)) && (BSD >= 199103)
291 if (setlogin(usernm) < 0) { 291 if (setlogin(usernm) < 0) {
292 syslog(LOG_ERR, "setlogin(%s) failure for %s: %m", 292 syslog(LOG_ERR, "setlogin(%s) failure for %s: %m",
293 usernm, e->pwd->pw_name); 293 usernm, e->pwd->pw_name);
294 _exit(ERROR_EXIT); 294 _exit(ERROR_EXIT);
295 } 295 }
296#endif /* BSD */ 296#endif /* BSD */
297#ifdef USE_PAM 297#ifdef USE_PAM
298 if (!cron_pam_setcred()) 298 if (!cron_pam_setcred())
299 _exit(ERROR_EXIT); 299 _exit(ERROR_EXIT);
300 cron_pam_child_close(); 300 cron_pam_child_close();
301#endif 301#endif
302 if (setuid(e->pwd->pw_uid) != 0) { 302 if (setuid(e->pwd->pw_uid) != 0) {
303 syslog(LOG_ERR, "setuid(%d) failed for %s: %m", 303 syslog(LOG_ERR, "setuid(%d) failed for %s: %m",
304 e->pwd->pw_uid, e->pwd->pw_name); 304 e->pwd->pw_uid, e->pwd->pw_name);
305 _exit(ERROR_EXIT); 305 _exit(ERROR_EXIT);
306 } 306 }
307 /* we aren't root after this... */ 307 /* we aren't root after this... */
308#endif /* LOGIN_CAP */ 308#endif /* LOGIN_CAP */
309 homedir = env_get("HOME", envp); 309 homedir = env_get("HOME", envp);
310 if (chdir(homedir) != 0) { 310 if (chdir(homedir) != 0) {
311 syslog(LOG_ERR, "chdir(%s) $HOME failed for %s: %m", 311 syslog(LOG_ERR, "chdir(%s) $HOME failed for %s: %m",
312 homedir, e->pwd->pw_name); 312 homedir, e->pwd->pw_name);
313 _exit(ERROR_EXIT); 313 _exit(ERROR_EXIT);
314 } 314 }
315 315
316#ifdef USE_SIGCHLD 316#ifdef USE_SIGCHLD
317 /* our grandparent is watching for our death by catching 317 /* our grandparent is watching for our death by catching
318 * SIGCHLD. the parent is ignoring SIGCHLD's; we want 318 * SIGCHLD. the parent is ignoring SIGCHLD's; we want
319 * to restore default behaviour. 319 * to restore default behaviour.
320 */ 320 */
321 (void) signal(SIGCHLD, SIG_DFL); 321 (void) signal(SIGCHLD, SIG_DFL);
322#endif 322#endif
323 (void) signal(SIGPIPE, SIG_DFL); 323 (void) signal(SIGPIPE, SIG_DFL);
324 (void) signal(SIGUSR1, SIG_DFL); 324 (void) signal(SIGUSR1, SIG_DFL);
325 (void) signal(SIGHUP, SIG_DFL); 325 (void) signal(SIGHUP, SIG_DFL);
326 326
327 /* 327 /*
328 * Exec the command. 328 * Exec the command.
329 */ 329 */
330 { 330 {
331 char *shell = env_get("SHELL", envp); 331 char *shell = env_get("SHELL", envp);
332 332
333# if DEBUGGING 333# if DEBUGGING
334 if (DebugFlags & DTEST) { 334 if (DebugFlags & DTEST) {
335 (void)fprintf(stderr, 335 (void)fprintf(stderr,
336 "debug DTEST is on, not exec'ing command.\n"); 336 "debug DTEST is on, not exec'ing command.\n");
337 (void)fprintf(stderr, 337 (void)fprintf(stderr,
338 "\tcmd='%s' shell='%s'\n", e->cmd, shell); 338 "\tcmd='%s' shell='%s'\n", e->cmd, shell);
339 _exit(OK_EXIT); 339 _exit(OK_EXIT);
340 } 340 }
341# endif /*DEBUGGING*/ 341# endif /*DEBUGGING*/
342 (void)execle(shell, shell, "-c", e->cmd, NULL, envp); 342 (void)execle(shell, shell, "-c", e->cmd, NULL, envp);
343 warn("execl: couldn't exec `%s'", shell); 343 warn("execl: couldn't exec `%s'", shell);
344 _exit(ERROR_EXIT); 344 _exit(ERROR_EXIT);
345 } 345 }
346 break; 346 break;
347 default: 347 default:
348 /* parent process */ 348 /* parent process */
349 break; 349 break;
350 } 350 }
351 351
352 /* middle process, child of original cron, parent of process running 352 /* middle process, child of original cron, parent of process running
353 * the user's command. 353 * the user's command.
354 */ 354 */
355 355
356 Debug(DPROC, ("[%ld] child continues, closing pipes\n",(long)getpid())); 356 Debug(DPROC, ("[%ld] child continues, closing pipes\n",(long)getpid()));
357 357
358 /* close the ends of the pipe that will only be referenced in the 358 /* close the ends of the pipe that will only be referenced in the
359 * grandchild process... 359 * grandchild process...
360 */ 360 */
361 (void)close(stdin_pipe[READ_PIPE]); 361 (void)close(stdin_pipe[READ_PIPE]);
362 (void)close(stdout_pipe[WRITE_PIPE]); 362 (void)close(stdout_pipe[WRITE_PIPE]);
363 363
364 /* 364 /*
365 * write, to the pipe connected to child's stdin, any input specified 365 * write, to the pipe connected to child's stdin, any input specified
366 * after a % in the crontab entry. while we copy, convert any 366 * after a % in the crontab entry. while we copy, convert any
367 * additional %'s to newlines. when done, if some characters were 367 * additional %'s to newlines. when done, if some characters were
368 * written and the last one wasn't a newline, write a newline. 368 * written and the last one wasn't a newline, write a newline.
369 * 369 *
370 * Note that if the input data won't fit into one pipe buffer (2K 370 * Note that if the input data won't fit into one pipe buffer (2K
371 * or 4K on most BSD systems), and the child doesn't read its stdin, 371 * or 4K on most BSD systems), and the child doesn't read its stdin,
372 * we would block here. thus we must fork again. 372 * we would block here. thus we must fork again.
373 */ 373 */
374 374
375 if (*input_data && fork() == 0) { 375 if (*input_data && fork() == 0) {
376 FILE *out = fdopen(stdin_pipe[WRITE_PIPE], "w"); 376 FILE *out = fdopen(stdin_pipe[WRITE_PIPE], "w");
377 int need_newline = FALSE; 377 int need_newline = FALSE;
378 int escaped = FALSE; 378 int escaped = FALSE;
379 int ch; 379 int ch;
380 380
381 Debug(DPROC, ("[%ld] child2 sending data to grandchild\n", 381 Debug(DPROC, ("[%ld] child2 sending data to grandchild\n",
382 (long)getpid())); 382 (long)getpid()));
383 383
384#ifdef USE_PAM 384#ifdef USE_PAM
385 cron_pam_child_close(); 385 cron_pam_child_close();
386#else 386#else
387 log_close(); 387 log_close();
388#endif 388#endif
389 389
390 /* close the pipe we don't use, since we inherited it and 390 /* close the pipe we don't use, since we inherited it and
391 * are part of its reference count now. 391 * are part of its reference count now.
392 */ 392 */
393 (void)close(stdout_pipe[READ_PIPE]); 393 (void)close(stdout_pipe[READ_PIPE]);
394 394
395 /* translation: 395 /* translation:
396 * \% -> % 396 * \% -> %
397 * % -> \n 397 * % -> \n
398 * \x -> \x for all x != % 398 * \x -> \x for all x != %
399 */ 399 */
400 while ((ch = *input_data++) != '\0') { 400 while ((ch = *input_data++) != '\0') {
401 if (escaped) { 401 if (escaped) {
402 if (ch != '%') 402 if (ch != '%')
403 (void)putc('\\', out); 403 (void)putc('\\', out);
404 } else { 404 } else {
405 if (ch == '%') 405 if (ch == '%')
406 ch = '\n'; 406 ch = '\n';
407 } 407 }
408 408
409 if (!(escaped = (ch == '\\'))) { 409 if (!(escaped = (ch == '\\'))) {
410 (void)putc(ch, out); 410 (void)putc(ch, out);
411 need_newline = (ch != '\n'); 411 need_newline = (ch != '\n');
412 } 412 }
413 } 413 }
414 if (escaped) 414 if (escaped)
415 (void)putc('\\', out); 415 (void)putc('\\', out);
416 if (need_newline) 416 if (need_newline)
417 (void)putc('\n', out); 417 (void)putc('\n', out);
418 418
419 /* close the pipe, causing an EOF condition. fclose causes 419 /* close the pipe, causing an EOF condition. fclose causes
420 * stdin_pipe[WRITE_PIPE] to be closed, too. 420 * stdin_pipe[WRITE_PIPE] to be closed, too.
421 */ 421 */
422 (void)fclose(out); 422 (void)fclose(out);
423 423
424 Debug(DPROC, ("[%ld] child2 done sending to grandchild\n", 424 Debug(DPROC, ("[%ld] child2 done sending to grandchild\n",
425 (long)getpid())); 425 (long)getpid()));
426 exit(0); 426 exit(0);
427 } 427 }
428 428
429 /* close the pipe to the grandkiddie's stdin, since its wicked uncle 429 /* close the pipe to the grandkiddie's stdin, since its wicked uncle
430 * ernie back there has it open and will close it when he's done. 430 * ernie back there has it open and will close it when he's done.
431 */ 431 */
432 (void)close(stdin_pipe[WRITE_PIPE]); 432 (void)close(stdin_pipe[WRITE_PIPE]);
433 433
434 /* 434 /*
435 * read output from the grandchild. it's stderr has been redirected to 435 * read output from the grandchild. it's stderr has been redirected to
436 * it's stdout, which has been redirected to our pipe. if there is any 436 * it's stdout, which has been redirected to our pipe. if there is any
437 * output, we'll be mailing it to the user whose crontab this is... 437 * output, we'll be mailing it to the user whose crontab this is...
438 * when the grandchild exits, we'll get EOF. 438 * when the grandchild exits, we'll get EOF.
439 */ 439 */
440 440
441 Debug(DPROC, ("[%ld] child reading output from grandchild\n", 441 Debug(DPROC, ("[%ld] child reading output from grandchild\n",
442 (long)getpid())); 442 (long)getpid()));
443 443
444 /*local*/{ 444 /*local*/{
445 FILE *in = fdopen(stdout_pipe[READ_PIPE], "r"); 445 FILE *in = fdopen(stdout_pipe[READ_PIPE], "r");
446 int ch = getc(in); 446 int ch = getc(in);
447 447
448 if (ch != EOF) { 448 if (ch != EOF) {
449 FILE *mail = NULL; 449 FILE *mail = NULL;
450 int bytes = 1; 450 int bytes = 1;
451 int status = 0; 451 int status = 0;
452 452
453 Debug(DPROC|DEXT, 453 Debug(DPROC|DEXT,
454 ("[%ld] got data (%x:%c) from grandchild\n", 454 ("[%ld] got data (%x:%c) from grandchild\n",
455 (long)getpid(), ch, ch)); 455 (long)getpid(), ch, ch));
456 456
457 /* get name of recipient. this is MAILTO if set to a 457 /* get name of recipient. this is MAILTO if set to a
458 * valid local username; USER otherwise. 458 * valid local username; USER otherwise.
459 */ 459 */
460 if (mailto) { 460 if (mailto) {
461 /* MAILTO was present in the environment 461 /* MAILTO was present in the environment
462 */ 462 */
463 if (!*mailto) { 463 if (!*mailto) {
464 /* ... but it's empty. set to NULL 464 /* ... but it's empty. set to NULL
465 */ 465 */
466 mailto = NULL; 466 mailto = NULL;
467 } 467 }
468 } else { 468 } else {
469 /* MAILTO not present, set to USER. 469 /* MAILTO not present, set to USER.
470 */ 470 */
471 mailto = usernm; 471 mailto = usernm;
472 } 472 }
473  473
474 /* if we are supposed to be mailing, MAILTO will 474 /* if we are supposed to be mailing, MAILTO will
475 * be non-NULL. only in this case should we set 475 * be non-NULL. only in this case should we set
476 * up the mail command and subjects and stuff... 476 * up the mail command and subjects and stuff...
477 */ 477 */
478 478
479 if (mailto && safe_p(usernm, mailto)) { 479 if (mailto && safe_p(usernm, mailto)) {
480 char **env; 480 char **env;
481 char mailcmd[MAX_COMMAND]; 481 char mailcmd[MAX_COMMAND];
482 char hostname[MAXHOSTNAMELEN + 1]; 482 char hostname[MAXHOSTNAMELEN + 1];
483 483
484 (void)gethostname(hostname, MAXHOSTNAMELEN); 484 (void)gethostname(hostname, MAXHOSTNAMELEN);
485 if (strlens(MAILFMT, MAILARG, NULL) + 1 485 if (strlens(MAILFMT, MAILARG, NULL) + 1
486 >= sizeof mailcmd) { 486 >= sizeof mailcmd) {
487 warnx("mailcmd too long"); 487 log_it(usernm, getpid(), "MAIL",
 488 "mailcmd too long");
488 retval = ERROR_EXIT; 489 retval = ERROR_EXIT;
489 goto child_process_end; 490 goto child_process_end;
490 } 491 }
491 (void)snprintf(mailcmd, sizeof(mailcmd),  492 (void)snprintf(mailcmd, sizeof(mailcmd),
492 MAILFMT, MAILARG); 493 MAILFMT, MAILARG);
493 if (!(mail = cron_popen(mailcmd, "w", e->pwd))) { 494 if (!(mail = cron_popen(mailcmd, "w", e->pwd))) {
494 warn("cannot run `%s'", mailcmd); 495 log_itx(usernm, getpid(), "MAIL",
 496 "cannot run `%s'", mailcmd);
495 retval = ERROR_EXIT; 497 retval = ERROR_EXIT;
496 goto child_process_end; 498 goto child_process_end;
497 } 499 }
498 (void)fprintf(mail, 500 (void)fprintf(mail,
499 "From: root (Cron Daemon)\n"); 501 "From: root (Cron Daemon)\n");
500 (void)fprintf(mail, "To: %s\n", mailto); 502 (void)fprintf(mail, "To: %s\n", mailto);
501 (void)fprintf(mail, 503 (void)fprintf(mail,
502 "Subject: Cron <%s@%s> %s\n", 504 "Subject: Cron <%s@%s> %s\n",
503 usernm, hostname, e->cmd); 505 usernm, hostname, e->cmd);
504 (void)fprintf(mail, 506 (void)fprintf(mail,
505 "Auto-Submitted: auto-generated\n"); 507 "Auto-Submitted: auto-generated\n");
506#ifdef MAIL_DATE 508#ifdef MAIL_DATE
507 (void)fprintf(mail, "Date: %s\n", 509 (void)fprintf(mail, "Date: %s\n",
508 arpadate(&StartTime)); 510 arpadate(&StartTime));
509#endif /*MAIL_DATE*/ 511#endif /*MAIL_DATE*/
510 for (env = envp; *env; env++) 512 for (env = envp; *env; env++)
511 (void)fprintf(mail, 513 (void)fprintf(mail,
512 "X-Cron-Env: <%s>\n", *env); 514 "X-Cron-Env: <%s>\n", *env);
513 (void)fprintf(mail, "\n"); 515 (void)fprintf(mail, "\n");
514 516
515 /* this was the first char from the pipe 517 /* this was the first char from the pipe
516 */ 518 */
517 (void)putc(ch, mail); 519 (void)putc(ch, mail);
518 } 520 }
519 521
520 /* we have to read the input pipe no matter whether 522 /* we have to read the input pipe no matter whether
521 * we mail or not, but obviously we only write to 523 * we mail or not, but obviously we only write to
522 * mail pipe if we ARE mailing. 524 * mail pipe if we ARE mailing.
523 */ 525 */
524 526
525 while (EOF != (ch = getc(in))) { 527 while (EOF != (ch = getc(in))) {
526 bytes++; 528 bytes++;
527 if (mailto) 529 if (mailto)
528 (void)putc(ch, mail); 530 (void)putc(ch, mail);
529 } 531 }
530 532
531 /* only close pipe if we opened it -- i.e., we're 533 /* only close pipe if we opened it -- i.e., we're
532 * mailing... 534 * mailing...
533 */ 535 */
534 536
535 if (mailto) { 537 if (mailto) {
536 Debug(DPROC, ("[%ld] closing pipe to mail\n", 538 Debug(DPROC, ("[%ld] closing pipe to mail\n",
537 (long)getpid())); 539 (long)getpid()));
538 /* Note: the pclose will probably see 540 /* Note: the pclose will probably see
539 * the termination of the grandchild 541 * the termination of the grandchild
540 * in addition to the mail process, since 542 * in addition to the mail process, since
541 * it (the grandchild) is likely to exit 543 * it (the grandchild) is likely to exit
542 * after closing its stdout. 544 * after closing its stdout.
543 */ 545 */
544 status = cron_pclose(mail); 546 status = cron_pclose(mail);
545 } 547 }
546 548
547 /* if there was output and we could not mail it, 549 /* if there was output and we could not mail it,
548 * log the facts so the poor user can figure out 550 * log the facts so the poor user can figure out
549 * what's going on. 551 * what's going on.
550 */ 552 */
551 if (mailto && status) { 553 if (mailto && status) {
552 char buf[MAX_TEMPSTR]; 554 log_itx(usernm, getpid(), "MAIL",
553 555 "mailed %d byte%s of output but got status"
554 (void)snprintf(buf, sizeof(buf), 556 " %#04x", bytes, bytes == 1 ? "" : "s",
555 "mailed %d byte%s of output but got status 0x%04x\n", 557 status);
556 bytes, (bytes==1)?"":"s", 
557 status); 
558 log_it(usernm, getpid(), "MAIL", buf); 
559 } 558 }
560 559
561 } /*if data from grandchild*/ 560 } /*if data from grandchild*/
562 561
563 Debug(DPROC, ("[%ld] got EOF from grandchild\n", 562 Debug(DPROC, ("[%ld] got EOF from grandchild\n",
564 (long)getpid())); 563 (long)getpid()));
565 564
566 (void)fclose(in); /* also closes stdout_pipe[READ_PIPE] */ 565 (void)fclose(in); /* also closes stdout_pipe[READ_PIPE] */
567 } 566 }
568 567
569 /* wait for children to die. 568 /* wait for children to die.
570 */ 569 */
571 sigchld_handler(0); 570 sigchld_handler(0);
572 571
573 /* Log the time when we finished deadling with the job */ 572 /* Log the time when we finished deadling with the job */
574 /*local*/{ 573 /*local*/{
575 char *x = mkprints(e->cmd, strlen(e->cmd)); 574 char *x = mkprints(e->cmd, strlen(e->cmd));
576 575
577 log_it(usernm, getpid(), "CMD FINISH", x); 576 log_it(usernm, getpid(), "CMD FINISH", x);
578 free(x); 577 free(x);
579 } 578 }
580 579
581child_process_end: 580child_process_end:
582#ifdef USE_PAM 581#ifdef USE_PAM
583 cron_pam_finish(); 582 cron_pam_finish();
584#endif 583#endif
585 return retval; 584 return retval;
586} 585}
587 586
588static int 587static int
589safe_p(const char *usernm, const char *s) { 588safe_p(const char *usernm, const char *s) {
590 static const char safe_delim[] = "@!:%-.,"; /* conservative! */ 589 static const char safe_delim[] = "@!:%-.,"; /* conservative! */
591 const char *t; 590 const char *t;
592 int ch, first; 591 int ch, first;
593 592
594 for (t = s, first = 1; (ch = *t++) != '\0'; first = 0) { 593 for (t = s, first = 1; (ch = *t++) != '\0'; first = 0) {
595 if (isascii(ch) && isprint(ch) && 594 if (isascii(ch) && isprint(ch) &&
596 (isalnum(ch) || (!first && strchr(safe_delim, ch)))) 595 (isalnum(ch) || (!first && strchr(safe_delim, ch))))
597 continue; 596 continue;
598 log_it(usernm, getpid(), "UNSAFE", s); 597 log_it(usernm, getpid(), "UNSAFE", s);
599 return (FALSE); 598 return (FALSE);
600 } 599 }
601 return (TRUE); 600 return (TRUE);
602} 601}

cvs diff -r1.4 -r1.5 src/external/bsd/cron/dist/funcs.h (switch to unified diff)

--- src/external/bsd/cron/dist/funcs.h 2017/06/09 17:36:30 1.4
+++ src/external/bsd/cron/dist/funcs.h 2017/08/17 08:53:00 1.5
@@ -1,86 +1,91 @@ @@ -1,86 +1,91 @@
1/* $NetBSD: funcs.h,v 1.4 2017/06/09 17:36:30 christos Exp $ */ 1/* $NetBSD: funcs.h,v 1.5 2017/08/17 08:53:00 christos Exp $ */
2 2
3/* 3/*
4 * Id: funcs.h,v 1.9 2004/01/23 18:56:42 vixie Exp 4 * Id: funcs.h,v 1.9 2004/01/23 18:56:42 vixie Exp
5 */ 5 */
6 6
7/* 7/*
8 * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") 8 * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
9 * Copyright (c) 1997,2000 by Internet Software Consortium, Inc. 9 * Copyright (c) 1997,2000 by Internet Software Consortium, Inc.
10 * 10 *
11 * Permission to use, copy, modify, and distribute this software for any 11 * Permission to use, copy, modify, and distribute this software for any
12 * purpose with or without fee is hereby granted, provided that the above 12 * purpose with or without fee is hereby granted, provided that the above
13 * copyright notice and this permission notice appear in all copies. 13 * copyright notice and this permission notice appear in all copies.
14 * 14 *
15 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES 15 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
16 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 16 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
17 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR 17 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
18 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 18 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 19 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
20 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 20 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
21 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 21 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22 */ 22 */
23 23
24/* Notes: 24/* Notes:
25 * This file has to be included by cron.h after data structure defs. 25 * This file has to be included by cron.h after data structure defs.
26 * We should reorg this into sections by module. 26 * We should reorg this into sections by module.
27 */ 27 */
28 28
29void set_cron_uid(void), 29void set_cron_uid(void),
30 set_cron_cwd(void), 30 set_cron_cwd(void),
31 load_database(cron_db *), 31 load_database(cron_db *),
32 open_logfile(void), 32 open_logfile(void),
33 sigpipe_func(void), 33 sigpipe_func(void),
34 job_add(entry *, user *, time_t), 34 job_add(entry *, user *, time_t),
35 do_command(entry *, user *), 35 do_command(entry *, user *),
36 link_user(cron_db *, user *), 36 link_user(cron_db *, user *),
37 unlink_user(cron_db *, user *), 37 unlink_user(cron_db *, user *),
38 free_user(user *), 38 free_user(user *),
39 env_free(char **), 39 env_free(char **),
40 unget_char(int, FILE *), 40 unget_char(int, FILE *),
41 free_entry(entry *), 41 free_entry(entry *),
42 acquire_daemonlock(int), 42 acquire_daemonlock(int),
43 skip_comments(FILE *), 43 skip_comments(FILE *),
44 log_it(const char *, int, const char *, const char *), 44 log_it(const char *, int, const char *, const char *),
45 log_close(void); 45 log_close(void);
46 46
 47
 48void
 49 log_itx(const char *, int, const char *, const char *, ...)
 50 __printflike(4, 5);
 51
47int job_runqueue(void), 52int job_runqueue(void),
48 set_debug_flags(const char *), 53 set_debug_flags(const char *),
49 get_char(FILE *), 54 get_char(FILE *),
50 get_string(char *, int, FILE *, const char *), 55 get_string(char *, int, FILE *, const char *),
51 load_env(char *, FILE *), 56 load_env(char *, FILE *),
52 cron_pclose(FILE *), 57 cron_pclose(FILE *),
53 glue_strings(char *, size_t, const char *, const char *, char), 58 glue_strings(char *, size_t, const char *, const char *, char),
54 strcmp_until(const char *, const char *, char), 59 strcmp_until(const char *, const char *, char),
55 strdtb(char *); 60 strdtb(char *);
56 61
57size_t strlens(const char *, ...); 62size_t strlens(const char *, ...);
58 63
59char *env_get(const char *, char **), 64char *env_get(const char *, char **),
60 *arpadate(time_t *), 65 *arpadate(time_t *),
61 *mkprints(char *, size_t), 66 *mkprints(char *, size_t),
62 **env_init(void), 67 **env_init(void),
63 **env_copy(char **), 68 **env_copy(char **),
64 **env_set(char **, char *); 69 **env_set(char **, char *);
65 70
66user *load_user(int, struct passwd *, const char *), 71user *load_user(int, struct passwd *, const char *),
67 *find_user(cron_db *, const char *); 72 *find_user(cron_db *, const char *);
68 73
69entry *load_entry(FILE *, void (*)(const char *), struct passwd *, char **); 74entry *load_entry(FILE *, void (*)(const char *), struct passwd *, char **);
70 75
71FILE *cron_popen(char *, const char *, struct passwd *); 76FILE *cron_popen(char *, const char *, struct passwd *);
72 77
73struct passwd *pw_dup(const struct passwd *); 78struct passwd *pw_dup(const struct passwd *);
74 79
75#ifndef HAVE_TM_GMTOFF 80#ifndef HAVE_TM_GMTOFF
76long get_gmtoff(time_t *, struct tm *); 81long get_gmtoff(time_t *, struct tm *);
77#endif 82#endif
78 83
79extern int close_all(int); 84extern int close_all(int);
80#ifdef USE_PAM 85#ifdef USE_PAM
81extern int cron_pam_start (const char *user); 86extern int cron_pam_start (const char *user);
82extern int cron_pam_setcred (void); 87extern int cron_pam_setcred (void);
83extern void cron_pam_finish (void); 88extern void cron_pam_finish (void);
84extern void cron_pam_child_close (void); 89extern void cron_pam_child_close (void);
85extern char **cron_pam_getenvlist (char **envp); 90extern char **cron_pam_getenvlist (char **envp);
86#endif 91#endif

cvs diff -r1.4 -r1.5 src/external/bsd/cron/dist/misc.c (switch to unified diff)

--- src/external/bsd/cron/dist/misc.c 2017/06/09 17:36:30 1.4
+++ src/external/bsd/cron/dist/misc.c 2017/08/17 08:53:00 1.5
@@ -1,625 +1,626 @@ @@ -1,625 +1,626 @@
1/* $NetBSD: misc.c,v 1.4 2017/06/09 17:36:30 christos Exp $ */ 1/* $NetBSD: misc.c,v 1.5 2017/08/17 08:53:00 christos Exp $ */
2 2
3/* Copyright 1988,1990,1993,1994 by Paul Vixie 3/* Copyright 1988,1990,1993,1994 by Paul Vixie
4 * All rights reserved 4 * All rights reserved
5 */ 5 */
6 6
7/* 7/*
8 * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") 8 * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
9 * Copyright (c) 1997,2000 by Internet Software Consortium, Inc. 9 * Copyright (c) 1997,2000 by Internet Software Consortium, Inc.
10 * 10 *
11 * Permission to use, copy, modify, and distribute this software for any 11 * Permission to use, copy, modify, and distribute this software for any
12 * purpose with or without fee is hereby granted, provided that the above 12 * purpose with or without fee is hereby granted, provided that the above
13 * copyright notice and this permission notice appear in all copies. 13 * copyright notice and this permission notice appear in all copies.
14 * 14 *
15 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES 15 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
16 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 16 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
17 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR 17 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
18 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 18 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 19 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
20 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 20 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
21 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 21 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22 */ 22 */
23#include <sys/cdefs.h> 23#include <sys/cdefs.h>
24#if !defined(lint) && !defined(LINT) 24#if !defined(lint) && !defined(LINT)
25#if 0 25#if 0
26static char rcsid[] = "Id: misc.c,v 1.16 2004/01/23 18:56:43 vixie Exp"; 26static char rcsid[] = "Id: misc.c,v 1.16 2004/01/23 18:56:43 vixie Exp";
27#else 27#else
28__RCSID("$NetBSD: misc.c,v 1.4 2017/06/09 17:36:30 christos Exp $"); 28__RCSID("$NetBSD: misc.c,v 1.5 2017/08/17 08:53:00 christos Exp $");
29#endif 29#endif
30#endif 30#endif
31 31
32/* vix 26jan87 [RCS has the rest of the log] 32/* vix 26jan87 [RCS has the rest of the log]
33 * vix 30dec86 [written] 33 * vix 30dec86 [written]
34 */ 34 */
35 35
36#include "cron.h" 36#include "cron.h"
37#include <limits.h> 37#include <limits.h>
38#include <vis.h> 38#include <vis.h>
39 39
40#if defined(SYSLOG) && defined(LOG_FILE) 40#if defined(SYSLOG) && defined(LOG_FILE)
41# undef LOG_FILE 41# undef LOG_FILE
42#endif 42#endif
43 43
44#if defined(LOG_DAEMON) && !defined(LOG_CRON) 44#if defined(LOG_DAEMON) && !defined(LOG_CRON)
45# define LOG_CRON LOG_DAEMON 45# define LOG_CRON LOG_DAEMON
46#endif 46#endif
47 47
48#ifndef FACILITY 48#ifndef FACILITY
49#define FACILITY LOG_CRON 49#define FACILITY LOG_CRON
50#endif 50#endif
51 51
52static int LogFD = ERR; 52static int LogFD = ERR;
53 53
54#if defined(SYSLOG) 54#if defined(SYSLOG)
55static int syslog_open = FALSE; 55static int syslog_open = FALSE;
56#endif 56#endif
57 57
58static void mkprint(char *, char *, size_t); 58static void mkprint(char *, char *, size_t);
59 59
60/* 60/*
61 * glue_strings is the overflow-safe equivalent of 61 * glue_strings is the overflow-safe equivalent of
62 * sprintf(buffer, "%s%c%s", a, separator, b); 62 * sprintf(buffer, "%s%c%s", a, separator, b);
63 * 63 *
64 * returns 1 on success, 0 on failure. 'buffer' MUST NOT be used if 64 * returns 1 on success, 0 on failure. 'buffer' MUST NOT be used if
65 * glue_strings fails. 65 * glue_strings fails.
66 */ 66 */
67int 67int
68glue_strings(char *buffer, size_t buffer_size, const char *a, const char *b, 68glue_strings(char *buffer, size_t buffer_size, const char *a, const char *b,
69 char separator) 69 char separator)
70{ 70{
71 char *buf; 71 char *buf;
72 char *buf_end; 72 char *buf_end;
73 73
74 if (buffer_size == 0) 74 if (buffer_size == 0)
75 return (0); 75 return (0);
76 buf_end = buffer + buffer_size; 76 buf_end = buffer + buffer_size;
77 buf = buffer; 77 buf = buffer;
78 78
79 for ( /* nothing */; buf < buf_end && *a != '\0'; buf++, a++ ) 79 for ( /* nothing */; buf < buf_end && *a != '\0'; buf++, a++ )
80 *buf = *a; 80 *buf = *a;
81 if (buf == buf_end) 81 if (buf == buf_end)
82 return (0); 82 return (0);
83 if (separator != '/' || buf == buffer || buf[-1] != '/') 83 if (separator != '/' || buf == buffer || buf[-1] != '/')
84 *buf++ = separator; 84 *buf++ = separator;
85 if (buf == buf_end) 85 if (buf == buf_end)
86 return (0); 86 return (0);
87 for ( /* nothing */; buf < buf_end && *b != '\0'; buf++, b++ ) 87 for ( /* nothing */; buf < buf_end && *b != '\0'; buf++, b++ )
88 *buf = *b; 88 *buf = *b;
89 if (buf == buf_end) 89 if (buf == buf_end)
90 return (0); 90 return (0);
91 *buf = '\0'; 91 *buf = '\0';
92 return (1); 92 return (1);
93} 93}
94 94
95int 95int
96strcmp_until(const char *left, const char *right, char until) { 96strcmp_until(const char *left, const char *right, char until) {
97 while (*left && *left != until && *left == *right) { 97 while (*left && *left != until && *left == *right) {
98 left++; 98 left++;
99 right++; 99 right++;
100 } 100 }
101 101
102 if ((*left=='\0' || *left == until) && 102 if ((*left=='\0' || *left == until) &&
103 (*right=='\0' || *right == until)) { 103 (*right=='\0' || *right == until)) {
104 return (0); 104 return (0);
105 } 105 }
106 return (*left - *right); 106 return (*left - *right);
107} 107}
108 108
109#ifdef notdef 109#ifdef notdef
110/* strdtb(s) - delete trailing blanks in string 's' and return new length 110/* strdtb(s) - delete trailing blanks in string 's' and return new length
111 */ 111 */
112int 112int
113strdtb(char *s) { 113strdtb(char *s) {
114 char *x = s; 114 char *x = s;
115 115
116 /* scan forward to the null 116 /* scan forward to the null
117 */ 117 */
118 while (*x) 118 while (*x)
119 x++; 119 x++;
120 120
121 /* scan backward to either the first character before the string, 121 /* scan backward to either the first character before the string,
122 * or the last non-blank in the string, whichever comes first. 122 * or the last non-blank in the string, whichever comes first.
123 */ 123 */
124 do {x--;} 124 do {x--;}
125 while (x >= s && isspace((unsigned char)*x)); 125 while (x >= s && isspace((unsigned char)*x));
126 126
127 /* one character beyond where we stopped above is where the null 127 /* one character beyond where we stopped above is where the null
128 * goes. 128 * goes.
129 */ 129 */
130 *++x = '\0'; 130 *++x = '\0';
131 131
132 /* the difference between the position of the null character and 132 /* the difference between the position of the null character and
133 * the position of the first character of the string is the length. 133 * the position of the first character of the string is the length.
134 */ 134 */
135 return (int)(x - s); 135 return (int)(x - s);
136} 136}
137#endif 137#endif
138 138
139int 139int
140set_debug_flags(const char *flags) { 140set_debug_flags(const char *flags) {
141 /* debug flags are of the form flag[,flag ...] 141 /* debug flags are of the form flag[,flag ...]
142 * 142 *
143 * if an error occurs, print a message to stdout and return FALSE. 143 * if an error occurs, print a message to stdout and return FALSE.
144 * otherwise return TRUE after setting ERROR_FLAGS. 144 * otherwise return TRUE after setting ERROR_FLAGS.
145 */ 145 */
146 146
147#if !DEBUGGING 147#if !DEBUGGING
148 148
149 printf("this program was compiled without debugging enabled\n"); 149 printf("this program was compiled without debugging enabled\n");
150 return (FALSE); 150 return (FALSE);
151 151
152#else /* DEBUGGING */ 152#else /* DEBUGGING */
153 153
154 const char *pc = flags; 154 const char *pc = flags;
155 155
156 DebugFlags = 0; 156 DebugFlags = 0;
157 157
158 while (*pc) { 158 while (*pc) {
159 const char * const *test; 159 const char * const *test;
160 int mask; 160 int mask;
161 161
162 /* try to find debug flag name in our list. 162 /* try to find debug flag name in our list.
163 */ 163 */
164 for (test = DebugFlagNames, mask = 1; 164 for (test = DebugFlagNames, mask = 1;
165 *test != NULL && strcmp_until(*test, pc, ','); 165 *test != NULL && strcmp_until(*test, pc, ',');
166 test++, mask <<= 1) 166 test++, mask <<= 1)
167 continue; 167 continue;
168 168
169 if (!*test) { 169 if (!*test) {
170 warnx("unrecognized debug flag <%s> <%s>\n", flags, pc); 170 warnx("unrecognized debug flag <%s> <%s>\n", flags, pc);
171 return (FALSE); 171 return (FALSE);
172 } 172 }
173 173
174 DebugFlags |= mask; 174 DebugFlags |= mask;
175 175
176 /* skip to the next flag 176 /* skip to the next flag
177 */ 177 */
178 while (*pc && *pc != ',') 178 while (*pc && *pc != ',')
179 pc++; 179 pc++;
180 if (*pc == ',') 180 if (*pc == ',')
181 pc++; 181 pc++;
182 } 182 }
183 183
184 if (DebugFlags) { 184 if (DebugFlags) {
185 int flag; 185 int flag;
186 186
187 (void)fprintf(stderr, "debug flags enabled:"); 187 (void)fprintf(stderr, "debug flags enabled:");
188 188
189 for (flag = 0; DebugFlagNames[flag]; flag++) 189 for (flag = 0; DebugFlagNames[flag]; flag++)
190 if (DebugFlags & (1 << flag)) 190 if (DebugFlags & (1 << flag))
191 (void)fprintf(stderr, " %s", DebugFlagNames[flag]); 191 (void)fprintf(stderr, " %s", DebugFlagNames[flag]);
192 (void)fprintf(stderr, "\n"); 192 (void)fprintf(stderr, "\n");
193 } 193 }
194 194
195 return (TRUE); 195 return (TRUE);
196 196
197#endif /* DEBUGGING */ 197#endif /* DEBUGGING */
198} 198}
199 199
200void 200void
201set_cron_uid(void) { 201set_cron_uid(void) {
202#if defined(BSD) || defined(POSIX) 202#if defined(BSD) || defined(POSIX)
203 if (seteuid(ROOT_UID) < OK) { 203 if (seteuid(ROOT_UID) < OK) {
204 err(ERROR_EXIT, "cannot seteuid"); 204 err(ERROR_EXIT, "cannot seteuid");
205 } 205 }
206#else 206#else
207 if (setuid(ROOT_UID) < OK) { 207 if (setuid(ROOT_UID) < OK) {
208 err(ERROR_EXIT, "cannot setuid"); 208 err(ERROR_EXIT, "cannot setuid");
209 } 209 }
210#endif 210#endif
211} 211}
212 212
213void 213void
214set_cron_cwd(void) { 214set_cron_cwd(void) {
215 struct stat sb; 215 struct stat sb;
216 struct group *grp = NULL; 216 struct group *grp = NULL;
217 217
218#ifdef CRON_GROUP 218#ifdef CRON_GROUP
219 grp = getgrnam(CRON_GROUP); 219 grp = getgrnam(CRON_GROUP);
220#endif 220#endif
221 /* first check for CRONDIR ("/var/cron" or some such) 221 /* first check for CRONDIR ("/var/cron" or some such)
222 */ 222 */
223#ifdef ENABLE_FIX_DIRECTORIES 223#ifdef ENABLE_FIX_DIRECTORIES
224 if (stat(CRONDIR, &sb) < OK && errno == ENOENT) { 224 if (stat(CRONDIR, &sb) < OK && errno == ENOENT) {
225 warn("Cannot stat `%s'", CRONDIR); 225 warn("Cannot stat `%s'", CRONDIR);
226 if (OK == mkdir(CRONDIR, 0710)) { 226 if (OK == mkdir(CRONDIR, 0710)) {
227 (void)fprintf(stderr, "%s: created\n", CRONDIR); 227 (void)fprintf(stderr, "%s: created\n", CRONDIR);
228 if (stat(CRONDIR, &sb) == -1) 228 if (stat(CRONDIR, &sb) == -1)
229 err(ERROR_EXIT, "cannot stat `%s'", CRONDIR); 229 err(ERROR_EXIT, "cannot stat `%s'", CRONDIR);
230 } else { 230 } else {
231 err(ERROR_EXIT, "cannot create `%s'", CRONDIR); 231 err(ERROR_EXIT, "cannot create `%s'", CRONDIR);
232 } 232 }
233 } 233 }
234 if (!S_ISDIR(sb.st_mode)) { 234 if (!S_ISDIR(sb.st_mode)) {
235 errx(ERROR_EXIT, "`%s' is not a directory, bailing out.", 235 errx(ERROR_EXIT, "`%s' is not a directory, bailing out.",
236 CRONDIR); 236 CRONDIR);
237 } 237 }
238#endif /* ENABLE_FIX_DIRECTORIES */ 238#endif /* ENABLE_FIX_DIRECTORIES */
239 if (chdir(CRONDIR) < OK) { 239 if (chdir(CRONDIR) < OK) {
240 err(ERROR_EXIT, "cannot chdir `%s', bailing out.\n", CRONDIR); 240 err(ERROR_EXIT, "cannot chdir `%s', bailing out.\n", CRONDIR);
241 } 241 }
242 242
243 /* CRONDIR okay (now==CWD), now look at SPOOL_DIR ("tabs" or some such) 243 /* CRONDIR okay (now==CWD), now look at SPOOL_DIR ("tabs" or some such)
244 */ 244 */
245#ifdef ENABLE_FIX_DIRECTORIES 245#ifdef ENABLE_FIX_DIRECTORIES
246 if (stat(SPOOL_DIR, &sb) < OK && errno == ENOENT) { 246 if (stat(SPOOL_DIR, &sb) < OK && errno == ENOENT) {
247 warn("cannot stat `%s'", SPOOL_DIR); 247 warn("cannot stat `%s'", SPOOL_DIR);
248 if (OK == mkdir(SPOOL_DIR, 0700)) { 248 if (OK == mkdir(SPOOL_DIR, 0700)) {
249 (void)fprintf(stderr, "%s: created\n", SPOOL_DIR); 249 (void)fprintf(stderr, "%s: created\n", SPOOL_DIR);
250 if (stat(SPOOL_DIR, &sb) == -1) 250 if (stat(SPOOL_DIR, &sb) == -1)
251 err(ERROR_EXIT, "cannot stat `%s'", CRONDIR); 251 err(ERROR_EXIT, "cannot stat `%s'", CRONDIR);
252 } else { 252 } else {
253 err(ERROR_EXIT, "cannot create `%s'", SPOOL_DIR); 253 err(ERROR_EXIT, "cannot create `%s'", SPOOL_DIR);
254 } 254 }
255 } 255 }
256#else 256#else
257 if (stat(SPOOL_DIR, &sb)) { 257 if (stat(SPOOL_DIR, &sb)) {
258 err(ERROR_EXIT, "cannot stat `%s'", SPOOL_DIR); 258 err(ERROR_EXIT, "cannot stat `%s'", SPOOL_DIR);
259 } 259 }
260#endif /* ENABLE_FIX_DIRECTORIES */ 260#endif /* ENABLE_FIX_DIRECTORIES */
261 if (!S_ISDIR(sb.st_mode)) { 261 if (!S_ISDIR(sb.st_mode)) {
262 errx(ERROR_EXIT, "`%s' is not a directory, bailing out.", 262 errx(ERROR_EXIT, "`%s' is not a directory, bailing out.",
263 SPOOL_DIR); 263 SPOOL_DIR);
264 } 264 }
265 if (grp != NULL) { 265 if (grp != NULL) {
266 if (sb.st_gid != grp->gr_gid) { 266 if (sb.st_gid != grp->gr_gid) {
267#ifdef ENABLE_FIX_DIRECTORIES 267#ifdef ENABLE_FIX_DIRECTORIES
268 errx(ERROR_EXIT, "Bad group %d != %d for `%s'", 268 errx(ERROR_EXIT, "Bad group %d != %d for `%s'",
269 (int)sb.st_gid, (int)grp->gr_gid, SPOOL_DIR); 269 (int)sb.st_gid, (int)grp->gr_gid, SPOOL_DIR);
270#else  270#else
271 if (chown(SPOOL_DIR, (uid_t)-1, grp->gr_gid) == -1) 271 if (chown(SPOOL_DIR, (uid_t)-1, grp->gr_gid) == -1)
272 err(ERROR_EXIT, "cannot chown `%s'", SPOOL_DIR); 272 err(ERROR_EXIT, "cannot chown `%s'", SPOOL_DIR);
273#endif 273#endif
274 } 274 }
275 if (sb.st_mode != 01730) 275 if (sb.st_mode != 01730)
276#ifdef ENABLE_FIX_DIRECTORIES 276#ifdef ENABLE_FIX_DIRECTORIES
277 errx(ERROR_EXIT, "Bad mode %#o != %#o for `%s'", 277 errx(ERROR_EXIT, "Bad mode %#o != %#o for `%s'",
278 (int)sb.st_mode, 01730, SPOOL_DIR); 278 (int)sb.st_mode, 01730, SPOOL_DIR);
279#else  279#else
280 if (chmod(SPOOL_DIR, 01730) == -1) 280 if (chmod(SPOOL_DIR, 01730) == -1)
281 err(ERROR_EXIT, "cannot chmod `%s'", SPOOL_DIR); 281 err(ERROR_EXIT, "cannot chmod `%s'", SPOOL_DIR);
282#endif 282#endif
283 } 283 }
284} 284}
285 285
286/* acquire_daemonlock() - write our PID into /etc/cron.pid, unless 286/* acquire_daemonlock() - write our PID into /etc/cron.pid, unless
287 * another daemon is already running, which we detect here. 287 * another daemon is already running, which we detect here.
288 * 288 *
289 * note: main() calls us twice; once before forking, once after. 289 * note: main() calls us twice; once before forking, once after.
290 * we maintain static storage of the file pointer so that we 290 * we maintain static storage of the file pointer so that we
291 * can rewrite our PID into _PATH_CRON_PID after the fork. 291 * can rewrite our PID into _PATH_CRON_PID after the fork.
292 */ 292 */
293void 293void
294acquire_daemonlock(int closeflag) { 294acquire_daemonlock(int closeflag) {
295 static int fd = -1; 295 static int fd = -1;
296 char buf[3*MAX_FNAME]; 296 char buf[3*MAX_FNAME];
297 const char *pidfile; 297 const char *pidfile;
298 char *ep; 298 char *ep;
299 long otherpid; 299 long otherpid;
300 ssize_t num; 300 ssize_t num;
301 301
302 if (closeflag) { 302 if (closeflag) {
303 /* close stashed fd for child so we don't leak it. */ 303 /* close stashed fd for child so we don't leak it. */
304 if (fd != -1) { 304 if (fd != -1) {
305 (void)close(fd); 305 (void)close(fd);
306 fd = -1; 306 fd = -1;
307 } 307 }
308 return; 308 return;
309 } 309 }
310 310
311 if (fd == -1) { 311 if (fd == -1) {
312 pidfile = _PATH_CRON_PID; 312 pidfile = _PATH_CRON_PID;
313 /* Initial mode is 0600 to prevent flock() race/DoS. */ 313 /* Initial mode is 0600 to prevent flock() race/DoS. */
314 if ((fd = open(pidfile, O_RDWR|O_CREAT, 0600)) == -1) { 314 if ((fd = open(pidfile, O_RDWR|O_CREAT, 0600)) == -1) {
315 (void)snprintf(buf, sizeof(buf), 315 log_itx("CRON", getpid(), "DEATH",
316 "can't open or create %s: %s", 316 "can't open or create %s: %s",
317 pidfile, strerror(errno)); 317 pidfile, strerror(errno));
318 log_it("CRON", getpid(), "DEATH", buf); 318 exit(ERROR_EXIT);
319 errx(ERROR_EXIT, "%s", buf); 
320 } 319 }
321 /* fd must be > STDERR since we dup fd 0-2 to /dev/null */ 320 /* fd must be > STDERR since we dup fd 0-2 to /dev/null */
322 if (fd <= STDERR) { 321 if (fd <= STDERR) {
323 if (dup2(fd, STDERR + 1) < 0) { 322 if (dup2(fd, STDERR + 1) < 0) {
324 snprintf(buf, sizeof buf, 323 log_itx("CRON", getpid(), "DEATH",
325 "can't dup pid fd: %s", strerror(errno)); 324 "can't dup pid fd: %s", strerror(errno));
326 log_it("CRON", getpid(), "DEATH", buf); 
327 exit(ERROR_EXIT); 325 exit(ERROR_EXIT);
328 } 326 }
329 close(fd); 327 close(fd);
330 fd = STDERR + 1; 328 fd = STDERR + 1;
331 } 329 }
332 330
333 if (flock(fd, LOCK_EX|LOCK_NB) < OK) { 331 if (flock(fd, LOCK_EX|LOCK_NB) < OK) {
334 int save_errno = errno; 332 int save_errno = errno;
335 333
336 memset(buf, 0, sizeof(buf)); 334 memset(buf, 0, sizeof(buf));
337 if ((num = read(fd, buf, sizeof(buf) - 1)) > 0 && 335 if ((num = read(fd, buf, sizeof(buf) - 1)) > 0 &&
338 (otherpid = strtol(buf, &ep, 10)) > 0 && 336 (otherpid = strtol(buf, &ep, 10)) > 0 &&
339 ep != buf && *ep == '\n' && otherpid != LONG_MAX) { 337 ep != buf && *ep == '\n' && otherpid != LONG_MAX) {
340 (void)snprintf(buf, sizeof(buf), 338 log_itx("CRON", getpid(), "DEATH",
341 "can't lock %s, otherpid may be %ld: %s", 339 "can't lock %s, otherpid may be %ld: %s",
342 pidfile, otherpid, strerror(save_errno)); 340 pidfile, otherpid, strerror(save_errno));
343 } else { 341 } else {
344 (void)snprintf(buf, sizeof(buf), 342 log_itx("CRON", getpid(), "DEATH",
345 "can't lock %s, otherpid unknown: %s", 343 "can't lock %s, otherpid unknown: %s",
346 pidfile, strerror(save_errno)); 344 pidfile, strerror(save_errno));
347 } 345 }
348 log_it("CRON", getpid(), "DEATH", buf); 346 exit(ERROR_EXIT);
349 errx(ERROR_EXIT, "%s", buf); 
350 } 347 }
351 (void) fchmod(fd, 0644); 348 (void) fchmod(fd, 0644);
352 (void) fcntl(fd, F_SETFD, 1); 349 (void) fcntl(fd, F_SETFD, 1);
353 } 350 }
354 351
355 (void)snprintf(buf, sizeof(buf), "%ld\n", (long)getpid()); 352 (void)snprintf(buf, sizeof(buf), "%ld\n", (long)getpid());
356 (void) lseek(fd, (off_t)0, SEEK_SET); 353 (void) lseek(fd, (off_t)0, SEEK_SET);
357 num = write(fd, buf, strlen(buf)); 354 num = write(fd, buf, strlen(buf));
358 (void) ftruncate(fd, num); 355 (void) ftruncate(fd, num);
359 356
360 /* abandon fd even though the file is open. we need to keep 357 /* abandon fd even though the file is open. we need to keep
361 * it open and locked, but we don't need the handles elsewhere. 358 * it open and locked, but we don't need the handles elsewhere.
362 */ 359 */
363} 360}
364 361
365/* get_char(file) : like getc() but increment LineNumber on newlines 362/* get_char(file) : like getc() but increment LineNumber on newlines
366 */ 363 */
367int 364int
368get_char(FILE *file) { 365get_char(FILE *file) {
369 int ch; 366 int ch;
370 367
371 ch = getc(file); 368 ch = getc(file);
372 if (ch == '\n') 369 if (ch == '\n')
373 Set_LineNum(LineNumber + 1); 370 Set_LineNum(LineNumber + 1);
374 return (ch); 371 return (ch);
375} 372}
376 373
377/* unget_char(ch, file) : like ungetc but do LineNumber processing 374/* unget_char(ch, file) : like ungetc but do LineNumber processing
378 */ 375 */
379void 376void
380unget_char(int ch, FILE *file) { 377unget_char(int ch, FILE *file) {
381 (void)ungetc(ch, file); 378 (void)ungetc(ch, file);
382 if (ch == '\n') 379 if (ch == '\n')
383 Set_LineNum(LineNumber - 1); 380 Set_LineNum(LineNumber - 1);
384} 381}
385 382
386/* get_string(str, max, file, termstr) : like fgets() but 383/* get_string(str, max, file, termstr) : like fgets() but
387 * (1) has terminator string which should include \n 384 * (1) has terminator string which should include \n
388 * (2) will always leave room for the null 385 * (2) will always leave room for the null
389 * (3) uses get_char() so LineNumber will be accurate 386 * (3) uses get_char() so LineNumber will be accurate
390 * (4) returns EOF or terminating character, whichever 387 * (4) returns EOF or terminating character, whichever
391 */ 388 */
392int 389int
393get_string(char *string, int size, FILE *file, const char *terms) { 390get_string(char *string, int size, FILE *file, const char *terms) {
394 int ch; 391 int ch;
395 392
396 while (EOF != (ch = get_char(file)) && !strchr(terms, ch)) { 393 while (EOF != (ch = get_char(file)) && !strchr(terms, ch)) {
397 if (size > 1) { 394 if (size > 1) {
398 *string++ = (char) ch; 395 *string++ = (char) ch;
399 size--; 396 size--;
400 } 397 }
401 } 398 }
402 399
403 if (size > 0) 400 if (size > 0)
404 *string = '\0'; 401 *string = '\0';
405 402
406 return (ch); 403 return (ch);
407} 404}
408 405
409/* skip_comments(file) : read past comment (if any) 406/* skip_comments(file) : read past comment (if any)
410 */ 407 */
411void 408void
412skip_comments(FILE *file) { 409skip_comments(FILE *file) {
413 int ch; 410 int ch;
414 411
415 while (EOF != (ch = get_char(file))) { 412 while (EOF != (ch = get_char(file))) {
416 /* ch is now the first character of a line. 413 /* ch is now the first character of a line.
417 */ 414 */
418 415
419 while (ch == ' ' || ch == '\t') 416 while (ch == ' ' || ch == '\t')
420 ch = get_char(file); 417 ch = get_char(file);
421 418
422 if (ch == EOF) 419 if (ch == EOF)
423 break; 420 break;
424 421
425 /* ch is now the first non-blank character of a line. 422 /* ch is now the first non-blank character of a line.
426 */ 423 */
427 424
428 if (ch != '\n' && ch != '#') 425 if (ch != '\n' && ch != '#')
429 break; 426 break;
430 427
431 /* ch must be a newline or comment as first non-blank 428 /* ch must be a newline or comment as first non-blank
432 * character on a line. 429 * character on a line.
433 */ 430 */
434 431
435 while (ch != '\n' && ch != EOF) 432 while (ch != '\n' && ch != EOF)
436 ch = get_char(file); 433 ch = get_char(file);
437 434
438 /* ch is now the newline of a line which we're going to 435 /* ch is now the newline of a line which we're going to
439 * ignore. 436 * ignore.
440 */ 437 */
441 } 438 }
442 if (ch != EOF) 439 if (ch != EOF)
443 unget_char(ch, file); 440 unget_char(ch, file);
444} 441}
445 442
446void 443void
 444log_itx(const char *username, PID_T xpid, const char *event, const char *fmt,
 445 ...)
 446{
 447 char *detail;
 448 va_list ap;
 449 va_start(ap, fmt);
 450 if (vasprintf(&detail, fmt, ap) == -1) {
 451 va_end(ap);
 452 return;
 453 }
 454 log_it(username, xpid, event, detail);
 455 free(detail);
 456}
 457
 458void
447log_it(const char *username, PID_T xpid, const char *event, const char *detail) { 459log_it(const char *username, PID_T xpid, const char *event, const char *detail) {
448#if defined(LOG_FILE) || DEBUGGING 460#if defined(LOG_FILE) || DEBUGGING
449 PID_T pid = xpid; 461 PID_T pid = xpid;
450#endif 462#endif
451#if defined(LOG_FILE) 463#if defined(LOG_FILE)
452 char *msg; 464 char *msg;
453 size_t msglen; 465 int msglen;
454 TIME_T now = time((TIME_T) 0); 466 TIME_T now = time((TIME_T) 0);
455 struct tm *t = localtime(&now); 467 struct tm *t = localtime(&now);
456#endif /*LOG_FILE*/ 
457 
458#if defined(LOG_FILE) 
459 /* we assume that MAX_TEMPSTR will hold the date, time, &punctuation. 
460 */ 
461 msglen = strlen(username) + strlen(event) + strlen(detail) + 
462 MAX_TEMPSTR); 
463 msg = malloc(msglen); 
464 if (msg == NULL) 
465 return; 
466 468
467 if (LogFD < OK) { 469 if (LogFD < OK) {
468 LogFD = open(LOG_FILE, O_WRONLY|O_APPEND|O_CREAT, 0600); 470 LogFD = open(LOG_FILE, O_WRONLY|O_APPEND|O_CREAT, 0600);
469 if (LogFD < OK) { 471 if (LogFD < OK) {
470 warn("can't open log file `%s'", LOG_FILE); 472 warn("can't open log file `%s'", LOG_FILE);
471 } else { 473 } else {
472 (void) fcntl(LogFD, F_SETFD, FD_CLOEXEC); 474 (void) fcntl(LogFD, F_SETFD, FD_CLOEXEC);
473 } 475 }
474 } 476 }
475 477
476 /* we have to sprintf() it because fprintf() doesn't always write 478 /* we have to sprintf() it because fprintf() doesn't always write
477 * everything out in one chunk and this has to be atomically appended 479 * everything out in one chunk and this has to be atomically appended
478 * to the log file. 480 * to the log file.
479 */ 481 */
480 (void)snprintf(msg, msglen, 482 msglen = asprintf(&msg,
481 "%s (%02d/%02d-%02d:%02d:%02d-%d) %s (%s)\n", 483 "%s (%02d/%02d-%02d:%02d:%02d-%d) %s (%s)\n", username,
482 username, 484 t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec, pid,
483 t->tm_mon+1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec, pid, 485 event, detail);
484 event, detail); 486 if (msglen == -1)
 487 return;
485 488
486 /* we have to run strlen() because sprintf() returns (char*) on old BSD 489 if (LogFD < OK || write(LogFD, msg, (size_t)msglen) < OK) {
487 */ 
488 if (LogFD < OK || write(LogFD, msg, strlen(msg)) < OK) { 
489 warn("can't write to log file"); 490 warn("can't write to log file");
490 write(STDERR, msg, strlen(msg)); 491 write(STDERR, msg, (size_t)msglen);
491 } 492 }
492 493
493 free(msg); 494 free(msg);
494#endif /*LOG_FILE*/ 495#endif /*LOG_FILE*/
495 496
496#if defined(SYSLOG) 497#if defined(SYSLOG)
497 if (!syslog_open) { 498 if (!syslog_open) {
498# ifdef LOG_DAEMON 499# ifdef LOG_DAEMON
499 openlog(getprogname(), LOG_PID, FACILITY); 500 openlog(getprogname(), LOG_PID, FACILITY);
500# else 501# else
501 openlog(getprogname(), LOG_PID); 502 openlog(getprogname(), LOG_PID);
502# endif 503# endif
503 syslog_open = TRUE; /* assume openlog success */ 504 syslog_open = TRUE; /* assume openlog success */
504 } 505 }
505 506
506 syslog(LOG_INFO, "(%s) %s (%s)", username, event, detail); 507 syslog(LOG_INFO, "(%s) %s (%s)", username, event, detail);
507 508
508#endif /*SYSLOG*/ 509#endif /*SYSLOG*/
509 510
510#if DEBUGGING 511#if DEBUGGING
511 if (DebugFlags) { 512 if (DebugFlags) {
512 (void)fprintf(stderr, "log_it: (%s %ld) %s (%s)\n", 513 (void)fprintf(stderr, "log_it: (%s %ld) %s (%s)\n",
513 username, (long)pid, event, detail); 514 username, (long)pid, event, detail);
514 } 515 }
515#endif 516#endif
516} 517}
517 518
518void 519void
519log_close(void) { 520log_close(void) {
520 if (LogFD != ERR) { 521 if (LogFD != ERR) {
521 (void)close(LogFD); 522 (void)close(LogFD);
522 LogFD = ERR; 523 LogFD = ERR;
523 } 524 }
524#if defined(SYSLOG) 525#if defined(SYSLOG)
525 closelog(); 526 closelog();
526 syslog_open = FALSE; 527 syslog_open = FALSE;
527#endif /*SYSLOG*/ 528#endif /*SYSLOG*/
528} 529}
529 530
530/* warning: 531/* warning:
531 * heavily ascii-dependent. 532 * heavily ascii-dependent.
532 */ 533 */
533static void 534static void
534mkprint(char *dst, char *src, size_t len) 535mkprint(char *dst, char *src, size_t len)
535{ 536{
536 while(len > 0 && isblank((unsigned char) *src)) 537 while(len > 0 && isblank((unsigned char) *src))
537 len--, src++; 538 len--, src++;
538 539
539 (void)strvisx(dst, src, len, VIS_TAB|VIS_NL); 540 (void)strvisx(dst, src, len, VIS_TAB|VIS_NL);
540} 541}
541 542
542/* warning: 543/* warning:
543 * returns a pointer to malloc'd storage, you must call free yourself. 544 * returns a pointer to malloc'd storage, you must call free yourself.
544 */ 545 */
545char * 546char *
546mkprints(char *src, size_t len) 547mkprints(char *src, size_t len)
547{ 548{
548 char *dst = malloc(len*4 + 1); 549 char *dst = malloc(len*4 + 1);
549 550
550 if (dst) 551 if (dst)
551 mkprint(dst, src, len); 552 mkprint(dst, src, len);
552 553
553 return (dst); 554 return (dst);
554} 555}
555 556
556#ifdef MAIL_DATE 557#ifdef MAIL_DATE
557/* Sat, 27 Feb 1993 11:44:51 -0800 (CST) 558/* Sat, 27 Feb 1993 11:44:51 -0800 (CST)
558 * 1234567890123456789012345678901234567 559 * 1234567890123456789012345678901234567
559 */ 560 */
560char * 561char *
561arpadate(time_t *clock) 562arpadate(time_t *clock)
562{ 563{
563 time_t t = clock ? *clock : time((TIME_T) 0); 564 time_t t = clock ? *clock : time((TIME_T) 0);
564 struct tm tm = *localtime(&t); 565 struct tm tm = *localtime(&t);
565 long gmtoff = get_gmtoff(&t, &tm); 566 long gmtoff = get_gmtoff(&t, &tm);
566 int hours = gmtoff / SECONDS_PER_HOUR; 567 int hours = gmtoff / SECONDS_PER_HOUR;
567 int minutes = (gmtoff - (hours * SECONDS_PER_HOUR)) / SECONDS_PER_MINUTE; 568 int minutes = (gmtoff - (hours * SECONDS_PER_HOUR)) / SECONDS_PER_MINUTE;
568 static char ret[64]; /* zone name might be >3 chars */ 569 static char ret[64]; /* zone name might be >3 chars */
569 570
570 if (minutes < 0) 571 if (minutes < 0)
571 minutes = -minutes; 572 minutes = -minutes;
572  573
573 (void)strftime(ret, sizeof(ret), "%a, %e %b %Y %T ????? (%Z)", &tm); 574 (void)strftime(ret, sizeof(ret), "%a, %e %b %Y %T ????? (%Z)", &tm);
574 (void)snprintf(strchr(ret, '?'), "% .2d%.2d", hours, minutes); 575 (void)snprintf(strchr(ret, '?'), "% .2d%.2d", hours, minutes);
575 ret[sizeof(ret) - 1] = '\0'; 576 ret[sizeof(ret) - 1] = '\0';
576 return ret; 577 return ret;
577} 578}
578#endif /*MAIL_DATE*/ 579#endif /*MAIL_DATE*/
579 580
580size_t 581size_t
581strlens(const char *last, ...) { 582strlens(const char *last, ...) {
582 va_list ap; 583 va_list ap;
583 size_t ret = 0; 584 size_t ret = 0;
584 const char *str; 585 const char *str;
585 586
586 va_start(ap, last); 587 va_start(ap, last);
587 for (str = last; str != NULL; str = va_arg(ap, const char *)) 588 for (str = last; str != NULL; str = va_arg(ap, const char *))
588 ret += strlen(str); 589 ret += strlen(str);
589 va_end(ap); 590 va_end(ap);
590 return (ret); 591 return (ret);
591} 592}
592 593
593/* Return the offset from GMT in seconds (algorithm taken from sendmail). 594/* Return the offset from GMT in seconds (algorithm taken from sendmail).
594 * 595 *
595 * warning: 596 * warning:
596 * clobbers the static storage space used by localtime() and gmtime(). 597 * clobbers the static storage space used by localtime() and gmtime().
597 * If the local pointer is non-NULL it *must* point to a local copy. 598 * If the local pointer is non-NULL it *must* point to a local copy.
598 */ 599 */
599#ifndef HAVE_TM_GMTOFF 600#ifndef HAVE_TM_GMTOFF
600long get_gmtoff(time_t *clock, struct tm *local) 601long get_gmtoff(time_t *clock, struct tm *local)
601{ 602{
602 struct tm gmt; 603 struct tm gmt;
603 long offset; 604 long offset;
604 605
605 gmt = *gmtime(clock); 606 gmt = *gmtime(clock);
606 if (local == NULL) 607 if (local == NULL)
607 local = localtime(clock); 608 local = localtime(clock);
608 609
609 offset = (local->tm_sec - gmt.tm_sec) + 610 offset = (local->tm_sec - gmt.tm_sec) +
610 ((local->tm_min - gmt.tm_min) * 60) + 611 ((local->tm_min - gmt.tm_min) * 60) +
611 ((local->tm_hour - gmt.tm_hour) * 3600); 612 ((local->tm_hour - gmt.tm_hour) * 3600);
612 613
613 /* Timezone may cause year rollover to happen on a different day. */ 614 /* Timezone may cause year rollover to happen on a different day. */
614 if (local->tm_year < gmt.tm_year) 615 if (local->tm_year < gmt.tm_year)
615 offset -= 24 * 3600; 616 offset -= 24 * 3600;
616 else if (local->tm_year > gmt.tm_year) 617 else if (local->tm_year > gmt.tm_year)
617 offset -= 24 * 3600; 618 offset -= 24 * 3600;
618 else if (local->tm_yday < gmt.tm_yday) 619 else if (local->tm_yday < gmt.tm_yday)
619 offset -= 24 * 3600; 620 offset -= 24 * 3600;
620 else if (local->tm_yday > gmt.tm_yday) 621 else if (local->tm_yday > gmt.tm_yday)
621 offset += 24 * 3600; 622 offset += 24 * 3600;
622 623
623 return (offset); 624 return (offset);
624} 625}
625#endif /* HAVE_TM_GMTOFF */ 626#endif /* HAVE_TM_GMTOFF */