GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/mail/popen.c Lines: 45 195 23.1 %
Date: 2017-11-07 Branches: 16 114 14.0 %

Line Branch Exec Source
1
/*	$OpenBSD: popen.c,v 1.38 2015/10/16 17:56:07 mmcc Exp $	*/
2
/*	$NetBSD: popen.c,v 1.6 1997/05/13 06:48:42 mikel Exp $	*/
3
4
/*
5
 * Copyright (c) 1980, 1993
6
 *	The Regents of the University of California.  All rights reserved.
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
 * 1. Redistributions of source code must retain the above copyright
12
 *    notice, this list of conditions and the following disclaimer.
13
 * 2. Redistributions in binary form must reproduce the above copyright
14
 *    notice, this list of conditions and the following disclaimer in the
15
 *    documentation and/or other materials provided with the distribution.
16
 * 3. Neither the name of the University nor the names of its contributors
17
 *    may be used to endorse or promote products derived from this software
18
 *    without specific prior written permission.
19
 *
20
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30
 * SUCH DAMAGE.
31
 */
32
33
#include "rcv.h"
34
#include <sys/wait.h>
35
#include <fcntl.h>
36
#include <errno.h>
37
#include <stdarg.h>
38
#include "extern.h"
39
40
#define READ 0
41
#define WRITE 1
42
43
struct fp {
44
	FILE *fp;
45
	int pipe;
46
	pid_t pid;
47
	struct fp *link;
48
};
49
static struct fp *fp_head;
50
51
struct child {
52
	pid_t pid;
53
	char done;
54
	char free;
55
	int status;
56
	struct child *link;
57
};
58
static struct child *child, *child_freelist = NULL;
59
60
static struct child *findchild(pid_t, int);
61
static void delchild(struct child *);
62
static pid_t file_pid(FILE *);
63
static int handle_spool_locks(int);
64
65
FILE *
66
Fopen(char *file, char *mode)
67
{
68
	FILE *fp;
69
70
12
	if ((fp = fopen(file, mode)) != NULL) {
71
4
		register_file(fp, 0, 0);
72
12
		(void)fcntl(fileno(fp), F_SETFD, FD_CLOEXEC);
73
4
	}
74
6
	return(fp);
75
}
76
77
FILE *
78
Fdopen(int fd, char *mode)
79
{
80
	FILE *fp;
81
82
8
	if ((fp = fdopen(fd, mode)) != NULL) {
83
4
		register_file(fp, 0, 0);
84
12
		(void)fcntl(fileno(fp), F_SETFD, FD_CLOEXEC);
85
4
	}
86
4
	return(fp);
87
}
88
89
int
90
Fclose(FILE *fp)
91
{
92
93
16
	unregister_file(fp);
94
8
	return(fclose(fp));
95
}
96
97
FILE *
98
Popen(char *cmd, char *mode)
99
{
100
	int p[2];
101
	int myside, hisside, fd0, fd1;
102
	pid_t pid;
103
	sigset_t nset;
104
	FILE *fp;
105
106
	if (pipe(p) < 0)
107
		return(NULL);
108
	(void)fcntl(p[READ], F_SETFD, FD_CLOEXEC);
109
	(void)fcntl(p[WRITE], F_SETFD, FD_CLOEXEC);
110
	if (*mode == 'r') {
111
		myside = p[READ];
112
		hisside = fd0 = fd1 = p[WRITE];
113
	} else {
114
		myside = p[WRITE];
115
		hisside = fd0 = p[READ];
116
		fd1 = -1;
117
	}
118
	sigemptyset(&nset);
119
	pid = start_command(value("SHELL"), &nset, fd0, fd1, "-c", cmd, NULL);
120
	if (pid < 0) {
121
		(void)close(p[READ]);
122
		(void)close(p[WRITE]);
123
		return(NULL);
124
	}
125
	(void)close(hisside);
126
	if ((fp = fdopen(myside, mode)) != NULL)
127
		register_file(fp, 1, pid);
128
	return(fp);
129
}
130
131
int
132
Pclose(FILE *ptr)
133
{
134
	int i;
135
	sigset_t nset, oset;
136
137
	i = file_pid(ptr);
138
	unregister_file(ptr);
139
	(void)fclose(ptr);
140
	sigemptyset(&nset);
141
	sigaddset(&nset, SIGINT);
142
	sigaddset(&nset, SIGHUP);
143
	sigprocmask(SIG_BLOCK, &nset, &oset);
144
	i = wait_child(i);
145
	sigprocmask(SIG_SETMASK, &oset, NULL);
146
	return(i);
147
}
148
149
void
150
close_all_files(void)
151
{
152
153
	while (fp_head)
154
		if (fp_head->pipe)
155
			(void)Pclose(fp_head->fp);
156
		else
157
			(void)Fclose(fp_head->fp);
158
}
159
160
void
161
register_file(FILE *fp, int pipe, pid_t pid)
162
{
163
	struct fp *fpp;
164
165
16
	if ((fpp = malloc(sizeof(*fpp))) == NULL)
166
		err(1, "malloc");
167
8
	fpp->fp = fp;
168
8
	fpp->pipe = pipe;
169
8
	fpp->pid = pid;
170
8
	fpp->link = fp_head;
171
8
	fp_head = fpp;
172
8
}
173
174
void
175
unregister_file(FILE *fp)
176
{
177
	struct fp **pp, *p;
178
179
32
	for (pp = &fp_head; (p = *pp) != NULL; pp = &p->link)
180
12
		if (p->fp == fp) {
181
8
			*pp = p->link;
182
8
			(void)free(p);
183
			return;
184
		}
185
	errx(1, "Invalid file pointer");
186
8
}
187
188
static pid_t
189
file_pid(FILE *fp)
190
{
191
	struct fp *p;
192
193
	for (p = fp_head; p; p = p->link)
194
		if (p->fp == fp)
195
			return(p->pid);
196
	errx(1, "Invalid file pointer");
197
	/*NOTREACHED*/
198
}
199
200
/*
201
 * Run a command without a shell, with optional arguments and splicing
202
 * of stdin (-1 means none) and stdout.  The command name can be a sequence
203
 * of words.
204
 * Signals must be handled by the caller.
205
 * "nset" contains the signals to ignore in the new process.
206
 * SIGINT is enabled unless it's in "nset".
207
 */
208
pid_t
209
start_commandv(char *cmd, sigset_t *nset, int infd, int outfd, va_list args)
210
{
211
	pid_t pid;
212
213
	if ((pid = fork()) < 0) {
214
		warn("fork");
215
		return(-1);
216
	}
217
	if (pid == 0) {
218
		char *argv[100];
219
		int i = getrawlist(cmd, argv, sizeof(argv)/ sizeof(*argv));
220
221
		while ((argv[i++] = va_arg(args, char *)))
222
			;
223
		argv[i] = NULL;
224
		prepare_child(nset, infd, outfd);
225
		execvp(argv[0], argv);
226
		warn("%s", argv[0]);
227
		_exit(1);
228
	}
229
	return(pid);
230
}
231
232
int
233
run_command(char *cmd, sigset_t *nset, int infd, int outfd, ...)
234
{
235
	pid_t pid;
236
	va_list args;
237
238
	va_start(args, outfd);
239
	pid = start_commandv(cmd, nset, infd, outfd, args);
240
	va_end(args);
241
	if (pid < 0)
242
		return(-1);
243
	return(wait_command(pid));
244
}
245
246
int
247
start_command(char *cmd, sigset_t *nset, int infd, int outfd, ...)
248
{
249
	va_list args;
250
	int r;
251
252
	va_start(args, outfd);
253
	r = start_commandv(cmd, nset, infd, outfd, args);
254
	va_end(args);
255
	return(r);
256
}
257
258
void
259
prepare_child(sigset_t *nset, int infd, int outfd)
260
{
261
	int i;
262
	sigset_t eset;
263
264
	/*
265
	 * All file descriptors other than 0, 1, and 2 are supposed to be
266
	 * close-on-exec.
267
	 */
268
	if (infd > 0) {
269
		dup2(infd, 0);
270
	} else if (infd != 0) {
271
		/* we don't want the child stealing my stdin input */
272
		close(0);
273
		open(_PATH_DEVNULL, O_RDONLY, 0);
274
	}
275
	if (outfd >= 0 && outfd != 1)
276
		dup2(outfd, 1);
277
	if (nset == NULL)
278
		return;
279
	if (nset != NULL) {
280
		for (i = 1; i < NSIG; i++)
281
			if (sigismember(nset, i))
282
				(void)signal(i, SIG_IGN);
283
	}
284
	if (nset == NULL || !sigismember(nset, SIGINT))
285
		(void)signal(SIGINT, SIG_DFL);
286
	sigemptyset(&eset);
287
	(void)sigprocmask(SIG_SETMASK, &eset, NULL);
288
}
289
290
int
291
wait_command(pid_t pid)
292
{
293
294
	if (wait_child(pid) < 0) {
295
		puts("Fatal error in process.");
296
		return(-1);
297
	}
298
	return(0);
299
}
300
301
static struct child *
302
findchild(pid_t pid, int dont_alloc)
303
{
304
	struct child **cpp;
305
306

6
	for (cpp = &child; *cpp != NULL && (*cpp)->pid != pid;
307
	     cpp = &(*cpp)->link)
308
			;
309
2
	if (*cpp == NULL) {
310
2
		if (dont_alloc)
311
			return(NULL);
312
2
		if (child_freelist) {
313
			*cpp = child_freelist;
314
			child_freelist = (*cpp)->link;
315
		} else {
316
2
			*cpp = malloc(sizeof(struct child));
317
2
			if (*cpp == NULL)
318
				err(1, "malloc");
319
		}
320
2
		(*cpp)->pid = pid;
321
2
		(*cpp)->done = (*cpp)->free = 0;
322
2
		(*cpp)->link = NULL;
323
2
	}
324
2
	return(*cpp);
325
2
}
326
327
static void
328
delchild(struct child *cp)
329
{
330
	struct child **cpp;
331
332
	for (cpp = &child; *cpp != cp; cpp = &(*cpp)->link)
333
		;
334
	*cpp = cp->link;
335
	cp->link = child_freelist;
336
	child_freelist = cp;
337
}
338
339
/* ARGSUSED */
340
void
341
sigchild(int signo)
342
{
343
	pid_t pid;
344
	int status;
345
	struct child *cp;
346
	int save_errno = errno;
347
348
	while ((pid = waitpid((pid_t)-1, &status, WNOHANG)) > 0) {
349
		cp = findchild(pid, 1);
350
		if (!cp)
351
			continue;
352
		if (cp->free)
353
			delchild(cp);
354
		else {
355
			cp->done = 1;
356
			cp->status = status;
357
		}
358
	}
359
	errno = save_errno;
360
}
361
362
int wait_status;
363
364
/*
365
 * Wait for a specific child to die.
366
 */
367
int
368
wait_child(pid_t pid)
369
{
370
	struct child *cp;
371
	sigset_t nset, oset;
372
	pid_t rv = 0;
373
374
	sigemptyset(&nset);
375
	sigaddset(&nset, SIGCHLD);
376
	sigprocmask(SIG_BLOCK, &nset, &oset);
377
	/*
378
	 * If we have not already waited on the pid (via sigchild)
379
	 * wait on it now.  Otherwise, use the wait status stashed
380
	 * by sigchild.
381
	 */
382
	cp = findchild(pid, 1);
383
	if (cp == NULL || !cp->done)
384
		rv = waitpid(pid, &wait_status, 0);
385
	else
386
		wait_status = cp->status;
387
	if (cp != NULL)
388
		delchild(cp);
389
	sigprocmask(SIG_SETMASK, &oset, NULL);
390
	if (rv == -1 || (WIFEXITED(wait_status) && WEXITSTATUS(wait_status)))
391
		return(-1);
392
	else
393
		return(0);
394
}
395
396
/*
397
 * Mark a child as don't care.
398
 */
399
void
400
free_child(pid_t pid)
401
{
402
	struct child *cp;
403
4
	sigset_t nset, oset;
404
405
2
	sigemptyset(&nset);
406
2
	sigaddset(&nset, SIGCHLD);
407
2
	sigprocmask(SIG_BLOCK, &nset, &oset);
408
2
	if ((cp = findchild(pid, 0)) != NULL) {
409
2
		if (cp->done)
410
			delchild(cp);
411
		else
412
2
			cp->free = 1;
413
	}
414
2
	sigprocmask(SIG_SETMASK, &oset, NULL);
415
2
}
416
417
/*
418
 * Lock(1)/unlock(0) mail spool using lockspool(1).
419
 * Returns 1 for success, 0 for failure, -1 for bad usage.
420
 */
421
static int
422
handle_spool_locks(int action)
423
{
424
	static FILE *lockfp = NULL;
425
426
	if (action == 0) {
427
		/* Clear the lock */
428
		if (lockfp == NULL) {
429
			fputs("handle_spool_locks: no spool lock to remove.\n",
430
			    stderr);
431
			return(-1);
432
		}
433
		(void)Pclose(lockfp);
434
		lockfp = NULL;
435
	} else if (action == 1) {
436
		char *cmd;
437
		char buf[sizeof(_PATH_LOCKSPOOL) + LOGIN_NAME_MAX + 1];
438
439
		/* XXX - lockspool requires root for user arg, we do not */
440
		if (uflag) {
441
			snprintf(buf, sizeof(buf), "%s %s", _PATH_LOCKSPOOL,
442
			    myname);
443
			cmd = buf;
444
		} else
445
			cmd = _PATH_LOCKSPOOL;
446
447
		/* Create the lock */
448
		lockfp = Popen(cmd, "r");
449
		if (lockfp == NULL)
450
			return(0);
451
		if (getc(lockfp) != '1') {
452
			Pclose(lockfp);
453
			lockfp = NULL;
454
			return(0);
455
		}
456
	} else {
457
		(void)fprintf(stderr, "handle_spool_locks: unknown action %d\n",
458
		    action);
459
		return(-1);
460
	}
461
462
	return(1);
463
}
464
465
int
466
spool_lock(void)
467
{
468
469
	return(handle_spool_locks(1));
470
}
471
472
int
473
spool_unlock(void)
474
{
475
476
	return(handle_spool_locks(0));
477
}