xref: /illumos-gate/usr/src/cmd/nohup/nohup.c (revision 7c478bd9)
1*7c478bd9Sstevel@tonic-gate /*
2*7c478bd9Sstevel@tonic-gate  * CDDL HEADER START
3*7c478bd9Sstevel@tonic-gate  *
4*7c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*7c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*7c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*7c478bd9Sstevel@tonic-gate  * with the License.
8*7c478bd9Sstevel@tonic-gate  *
9*7c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*7c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*7c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*7c478bd9Sstevel@tonic-gate  * and limitations under the License.
13*7c478bd9Sstevel@tonic-gate  *
14*7c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*7c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*7c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*7c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*7c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*7c478bd9Sstevel@tonic-gate  *
20*7c478bd9Sstevel@tonic-gate  * CDDL HEADER END
21*7c478bd9Sstevel@tonic-gate  */
22*7c478bd9Sstevel@tonic-gate /*
23*7c478bd9Sstevel@tonic-gate  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24*7c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
25*7c478bd9Sstevel@tonic-gate  */
26*7c478bd9Sstevel@tonic-gate 
27*7c478bd9Sstevel@tonic-gate /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
28*7c478bd9Sstevel@tonic-gate /*	  All Rights Reserved	*/
29*7c478bd9Sstevel@tonic-gate 
30*7c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
31*7c478bd9Sstevel@tonic-gate 
32*7c478bd9Sstevel@tonic-gate #include <stdio.h>
33*7c478bd9Sstevel@tonic-gate #include <stdlib.h>
34*7c478bd9Sstevel@tonic-gate #include <nl_types.h>
35*7c478bd9Sstevel@tonic-gate #include <locale.h>
36*7c478bd9Sstevel@tonic-gate #include <signal.h>
37*7c478bd9Sstevel@tonic-gate #include <string.h>
38*7c478bd9Sstevel@tonic-gate #include <limits.h>
39*7c478bd9Sstevel@tonic-gate #include <errno.h>
40*7c478bd9Sstevel@tonic-gate #include <unistd.h>
41*7c478bd9Sstevel@tonic-gate #include <sys/types.h>
42*7c478bd9Sstevel@tonic-gate #include <sys/stat.h>
43*7c478bd9Sstevel@tonic-gate #include <libproc.h>
44*7c478bd9Sstevel@tonic-gate #include <dirent.h>
45*7c478bd9Sstevel@tonic-gate #include <ctype.h>
46*7c478bd9Sstevel@tonic-gate #include <sys/time.h>
47*7c478bd9Sstevel@tonic-gate 
48*7c478bd9Sstevel@tonic-gate #define	NOHUP_PERM	(S_IRUSR | S_IWUSR)
49*7c478bd9Sstevel@tonic-gate 
50*7c478bd9Sstevel@tonic-gate #define	NOHUP_NOEXEC	126
51*7c478bd9Sstevel@tonic-gate #define	NOHUP_ERROR	127
52*7c478bd9Sstevel@tonic-gate 
53*7c478bd9Sstevel@tonic-gate #ifdef XPG4
54*7c478bd9Sstevel@tonic-gate #define	OPTSTR	""
55*7c478bd9Sstevel@tonic-gate #else
56*7c478bd9Sstevel@tonic-gate #define	OPTSTR	"pFag"
57*7c478bd9Sstevel@tonic-gate 
58*7c478bd9Sstevel@tonic-gate static int pnohup(int, char **);
59*7c478bd9Sstevel@tonic-gate 
60*7c478bd9Sstevel@tonic-gate static struct ps_prochandle *g_proc;
61*7c478bd9Sstevel@tonic-gate static int g_wrfd;
62*7c478bd9Sstevel@tonic-gate static int g_rdfd;
63*7c478bd9Sstevel@tonic-gate 
64*7c478bd9Sstevel@tonic-gate static int g_dirty;
65*7c478bd9Sstevel@tonic-gate static volatile int g_interrupt = 0;
66*7c478bd9Sstevel@tonic-gate #endif
67*7c478bd9Sstevel@tonic-gate 
68*7c478bd9Sstevel@tonic-gate static int opt_p = 0;
69*7c478bd9Sstevel@tonic-gate static int opt_g = 0;
70*7c478bd9Sstevel@tonic-gate static int opt_a = 0;
71*7c478bd9Sstevel@tonic-gate static int opt_F = 0;
72*7c478bd9Sstevel@tonic-gate 
73*7c478bd9Sstevel@tonic-gate static char *pname;
74*7c478bd9Sstevel@tonic-gate 
75*7c478bd9Sstevel@tonic-gate static char nout[PATH_MAX] = "nohup.out";
76*7c478bd9Sstevel@tonic-gate 
77*7c478bd9Sstevel@tonic-gate static int
open_file(void)78*7c478bd9Sstevel@tonic-gate open_file(void)
79*7c478bd9Sstevel@tonic-gate {
80*7c478bd9Sstevel@tonic-gate 	char *home;
81*7c478bd9Sstevel@tonic-gate 	int fd;
82*7c478bd9Sstevel@tonic-gate 	int flags = O_CREAT | O_WRONLY | O_APPEND;
83*7c478bd9Sstevel@tonic-gate 
84*7c478bd9Sstevel@tonic-gate 	if ((fd = open(nout, flags, NOHUP_PERM)) < 0) {
85*7c478bd9Sstevel@tonic-gate 		if ((home = getenv("HOME")) == NULL)
86*7c478bd9Sstevel@tonic-gate 			return (-1);
87*7c478bd9Sstevel@tonic-gate 
88*7c478bd9Sstevel@tonic-gate 		if ((snprintf(nout, sizeof (nout),
89*7c478bd9Sstevel@tonic-gate 		    "%s/nohup.out", home) >= sizeof (nout)) ||
90*7c478bd9Sstevel@tonic-gate 		    (fd = open(nout, flags, NOHUP_PERM)) < 0) {
91*7c478bd9Sstevel@tonic-gate 			return (-1);
92*7c478bd9Sstevel@tonic-gate 		}
93*7c478bd9Sstevel@tonic-gate 
94*7c478bd9Sstevel@tonic-gate 	}
95*7c478bd9Sstevel@tonic-gate 
96*7c478bd9Sstevel@tonic-gate 	(void) fprintf(stderr, gettext("Sending output to %s\n"), nout);
97*7c478bd9Sstevel@tonic-gate 
98*7c478bd9Sstevel@tonic-gate 	return (fd);
99*7c478bd9Sstevel@tonic-gate }
100*7c478bd9Sstevel@tonic-gate 
101*7c478bd9Sstevel@tonic-gate int
main(int argc,char ** argv)102*7c478bd9Sstevel@tonic-gate main(int argc, char **argv)
103*7c478bd9Sstevel@tonic-gate {
104*7c478bd9Sstevel@tonic-gate 	int fd = -1;
105*7c478bd9Sstevel@tonic-gate 	int opt;
106*7c478bd9Sstevel@tonic-gate 	int err;
107*7c478bd9Sstevel@tonic-gate 
108*7c478bd9Sstevel@tonic-gate 	if ((pname = strrchr(argv[0], '/')) == NULL)
109*7c478bd9Sstevel@tonic-gate 		pname = argv[0];
110*7c478bd9Sstevel@tonic-gate 	else
111*7c478bd9Sstevel@tonic-gate 		argv[0] = ++pname;		/* for getopt */
112*7c478bd9Sstevel@tonic-gate 
113*7c478bd9Sstevel@tonic-gate 	(void) setlocale(LC_ALL, "");
114*7c478bd9Sstevel@tonic-gate 
115*7c478bd9Sstevel@tonic-gate #ifndef TEXT_DOMAIN
116*7c478bd9Sstevel@tonic-gate #define	TEXT_DOMAIN "SYS_TEST"
117*7c478bd9Sstevel@tonic-gate #endif
118*7c478bd9Sstevel@tonic-gate 
119*7c478bd9Sstevel@tonic-gate 	(void) textdomain(TEXT_DOMAIN);
120*7c478bd9Sstevel@tonic-gate 
121*7c478bd9Sstevel@tonic-gate 	while ((opt = getopt(argc, argv, OPTSTR)) != EOF) {
122*7c478bd9Sstevel@tonic-gate 		switch (opt) {
123*7c478bd9Sstevel@tonic-gate 		case 'p':
124*7c478bd9Sstevel@tonic-gate 			opt_p = 1;
125*7c478bd9Sstevel@tonic-gate 			break;
126*7c478bd9Sstevel@tonic-gate 		case 'F':
127*7c478bd9Sstevel@tonic-gate 			opt_F = 1;
128*7c478bd9Sstevel@tonic-gate 			break;
129*7c478bd9Sstevel@tonic-gate 		case 'a':
130*7c478bd9Sstevel@tonic-gate 			opt_a = 1;
131*7c478bd9Sstevel@tonic-gate 			break;
132*7c478bd9Sstevel@tonic-gate 		case 'g':
133*7c478bd9Sstevel@tonic-gate 			opt_g = 1;
134*7c478bd9Sstevel@tonic-gate 			break;
135*7c478bd9Sstevel@tonic-gate 		default:
136*7c478bd9Sstevel@tonic-gate 			goto usage;
137*7c478bd9Sstevel@tonic-gate 		}
138*7c478bd9Sstevel@tonic-gate 	}
139*7c478bd9Sstevel@tonic-gate 
140*7c478bd9Sstevel@tonic-gate 	argc -= optind;
141*7c478bd9Sstevel@tonic-gate 	argv += optind;
142*7c478bd9Sstevel@tonic-gate 
143*7c478bd9Sstevel@tonic-gate 	if (argc == 0)
144*7c478bd9Sstevel@tonic-gate 		goto usage;			/* need at least one argument */
145*7c478bd9Sstevel@tonic-gate 
146*7c478bd9Sstevel@tonic-gate #ifndef XPG4
147*7c478bd9Sstevel@tonic-gate 	if (opt_p && opt_g)
148*7c478bd9Sstevel@tonic-gate 		goto usage;
149*7c478bd9Sstevel@tonic-gate 
150*7c478bd9Sstevel@tonic-gate 	if (opt_p || opt_g)
151*7c478bd9Sstevel@tonic-gate 		return (pnohup(argc, argv));
152*7c478bd9Sstevel@tonic-gate 
153*7c478bd9Sstevel@tonic-gate 	if (opt_a || opt_F)
154*7c478bd9Sstevel@tonic-gate 		goto usage;			/* only valid with -p or -g */
155*7c478bd9Sstevel@tonic-gate #endif
156*7c478bd9Sstevel@tonic-gate 
157*7c478bd9Sstevel@tonic-gate 	argv[argc] = NULL;
158*7c478bd9Sstevel@tonic-gate 
159*7c478bd9Sstevel@tonic-gate 	(void) signal(SIGHUP, SIG_IGN);		/* POSIX.2 only SIGHUP */
160*7c478bd9Sstevel@tonic-gate #ifndef XPG4
161*7c478bd9Sstevel@tonic-gate 	(void) signal(SIGQUIT, SIG_IGN);	/* Solaris compatibility */
162*7c478bd9Sstevel@tonic-gate #endif
163*7c478bd9Sstevel@tonic-gate 
164*7c478bd9Sstevel@tonic-gate 	if (isatty(STDOUT_FILENO)) {
165*7c478bd9Sstevel@tonic-gate 		if ((fd = open_file()) < 0)
166*7c478bd9Sstevel@tonic-gate 			goto err;
167*7c478bd9Sstevel@tonic-gate 
168*7c478bd9Sstevel@tonic-gate 		(void) dup2(fd, STDOUT_FILENO);
169*7c478bd9Sstevel@tonic-gate 	}
170*7c478bd9Sstevel@tonic-gate 
171*7c478bd9Sstevel@tonic-gate 	if (isatty(STDERR_FILENO)) {
172*7c478bd9Sstevel@tonic-gate 		if (fd < 0 && (fd = open_file()) < 0)
173*7c478bd9Sstevel@tonic-gate 			goto err;
174*7c478bd9Sstevel@tonic-gate 
175*7c478bd9Sstevel@tonic-gate 		(void) dup2(fd, STDERR_FILENO);
176*7c478bd9Sstevel@tonic-gate 	}
177*7c478bd9Sstevel@tonic-gate 
178*7c478bd9Sstevel@tonic-gate 	if (fd >= 0)
179*7c478bd9Sstevel@tonic-gate 		(void) close(fd);
180*7c478bd9Sstevel@tonic-gate 
181*7c478bd9Sstevel@tonic-gate 	(void) execvp(argv[0], argv);
182*7c478bd9Sstevel@tonic-gate 	err = errno;
183*7c478bd9Sstevel@tonic-gate 
184*7c478bd9Sstevel@tonic-gate 	(void) freopen("/dev/tty", "w", stderr);
185*7c478bd9Sstevel@tonic-gate 	(void) fprintf(stderr, gettext("nohup: %s: %s\n"), argv[0],
186*7c478bd9Sstevel@tonic-gate 	    strerror(err));
187*7c478bd9Sstevel@tonic-gate 
188*7c478bd9Sstevel@tonic-gate 	return (err == ENOENT ? NOHUP_ERROR : NOHUP_NOEXEC);
189*7c478bd9Sstevel@tonic-gate 
190*7c478bd9Sstevel@tonic-gate err:
191*7c478bd9Sstevel@tonic-gate 	(void) fprintf(stderr, gettext("nohup: cannot open/create "
192*7c478bd9Sstevel@tonic-gate 	    "nohup.out: %s\n"), strerror(errno));
193*7c478bd9Sstevel@tonic-gate 	return (NOHUP_ERROR);
194*7c478bd9Sstevel@tonic-gate 
195*7c478bd9Sstevel@tonic-gate usage:
196*7c478bd9Sstevel@tonic-gate #ifdef XPG4
197*7c478bd9Sstevel@tonic-gate 	(void) fprintf(stderr,
198*7c478bd9Sstevel@tonic-gate 	    gettext("usage: nohup command [argument ...]\n"));
199*7c478bd9Sstevel@tonic-gate #else
200*7c478bd9Sstevel@tonic-gate 	(void) fprintf(stderr, gettext("usage:\n"
201*7c478bd9Sstevel@tonic-gate 	    "\tnohup command [argument ...]\n"
202*7c478bd9Sstevel@tonic-gate 	    "\tnohup -p [-Fa] pid [pid ...]\n"
203*7c478bd9Sstevel@tonic-gate 	    "\tnohup -g [-Fa] pgid [pgid ...]\n"));
204*7c478bd9Sstevel@tonic-gate #endif
205*7c478bd9Sstevel@tonic-gate 	return (NOHUP_ERROR);
206*7c478bd9Sstevel@tonic-gate }
207*7c478bd9Sstevel@tonic-gate 
208*7c478bd9Sstevel@tonic-gate #ifndef XPG4
209*7c478bd9Sstevel@tonic-gate 
210*7c478bd9Sstevel@tonic-gate /*
211*7c478bd9Sstevel@tonic-gate  * File descriptor iteration interface.
212*7c478bd9Sstevel@tonic-gate  */
213*7c478bd9Sstevel@tonic-gate typedef int proc_fd_iter_f(void *, int);
214*7c478bd9Sstevel@tonic-gate 
215*7c478bd9Sstevel@tonic-gate static int
Pfd_iter(struct ps_prochandle * P,proc_fd_iter_f * cb,void * data)216*7c478bd9Sstevel@tonic-gate Pfd_iter(struct ps_prochandle *P, proc_fd_iter_f *cb, void *data)
217*7c478bd9Sstevel@tonic-gate {
218*7c478bd9Sstevel@tonic-gate 	char file[64];
219*7c478bd9Sstevel@tonic-gate 	dirent_t *dentp;
220*7c478bd9Sstevel@tonic-gate 	DIR *dirp;
221*7c478bd9Sstevel@tonic-gate 	int ret = 0;
222*7c478bd9Sstevel@tonic-gate 
223*7c478bd9Sstevel@tonic-gate 	if (Pstate(P) == PS_DEAD)
224*7c478bd9Sstevel@tonic-gate 		return (-1);
225*7c478bd9Sstevel@tonic-gate 
226*7c478bd9Sstevel@tonic-gate 	(void) sprintf(file, "/proc/%d/fd", (int)Pstatus(P)->pr_pid);
227*7c478bd9Sstevel@tonic-gate 	if ((dirp = opendir(file)) == NULL)
228*7c478bd9Sstevel@tonic-gate 		return (-1);
229*7c478bd9Sstevel@tonic-gate 
230*7c478bd9Sstevel@tonic-gate 	while ((dentp = readdir(dirp)) != NULL) {
231*7c478bd9Sstevel@tonic-gate 		if (dentp->d_name[0] == '.')
232*7c478bd9Sstevel@tonic-gate 			continue;
233*7c478bd9Sstevel@tonic-gate 
234*7c478bd9Sstevel@tonic-gate 		if ((ret = cb(data, atoi(dentp->d_name))) != 0)
235*7c478bd9Sstevel@tonic-gate 			break;
236*7c478bd9Sstevel@tonic-gate 	}
237*7c478bd9Sstevel@tonic-gate 
238*7c478bd9Sstevel@tonic-gate 	(void) closedir(dirp);
239*7c478bd9Sstevel@tonic-gate 
240*7c478bd9Sstevel@tonic-gate 	return (ret);
241*7c478bd9Sstevel@tonic-gate }
242*7c478bd9Sstevel@tonic-gate 
243*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
244*7c478bd9Sstevel@tonic-gate static int
fd_cb(void * data,int fd)245*7c478bd9Sstevel@tonic-gate fd_cb(void *data, int fd)
246*7c478bd9Sstevel@tonic-gate {
247*7c478bd9Sstevel@tonic-gate 	struct stat64 sbuf;
248*7c478bd9Sstevel@tonic-gate 	int flags;
249*7c478bd9Sstevel@tonic-gate 	int *fdp;
250*7c478bd9Sstevel@tonic-gate 	int oflags;
251*7c478bd9Sstevel@tonic-gate 	char *file;
252*7c478bd9Sstevel@tonic-gate 	int tmpfd;
253*7c478bd9Sstevel@tonic-gate 
254*7c478bd9Sstevel@tonic-gate 	/*
255*7c478bd9Sstevel@tonic-gate 	 * See if this fd refers to the controlling tty.
256*7c478bd9Sstevel@tonic-gate 	 */
257*7c478bd9Sstevel@tonic-gate 	if (pr_fstat64(g_proc, fd, &sbuf) == -1 ||
258*7c478bd9Sstevel@tonic-gate 	    sbuf.st_rdev != Ppsinfo(g_proc)->pr_ttydev)
259*7c478bd9Sstevel@tonic-gate 		return (0);
260*7c478bd9Sstevel@tonic-gate 
261*7c478bd9Sstevel@tonic-gate 	/*
262*7c478bd9Sstevel@tonic-gate 	 * tty's opened for input are usually O_RDWR so that the program
263*7c478bd9Sstevel@tonic-gate 	 * can change terminal settings. We assume that if there's a
264*7c478bd9Sstevel@tonic-gate 	 * controlling tty in the STDIN_FILENO file descriptor that is
265*7c478bd9Sstevel@tonic-gate 	 * effectively used only for input. If standard in gets dup'ed to
266*7c478bd9Sstevel@tonic-gate 	 * other file descriptors, then we're out of luck unless the
267*7c478bd9Sstevel@tonic-gate 	 * program is nice enough to fcntl it to be O_RDONLY. We close the
268*7c478bd9Sstevel@tonic-gate 	 * file descriptor before we call open to handle the case that
269*7c478bd9Sstevel@tonic-gate 	 * there are no available file descriptors left in the victim. If
270*7c478bd9Sstevel@tonic-gate 	 * our call to pr_open fails, we try to reopen the controlling tty.
271*7c478bd9Sstevel@tonic-gate 	 */
272*7c478bd9Sstevel@tonic-gate 	flags = pr_fcntl(g_proc, fd, F_GETFL, NULL);
273*7c478bd9Sstevel@tonic-gate 	if ((flags & O_ACCMODE) == O_RDONLY || fd == STDIN_FILENO) {
274*7c478bd9Sstevel@tonic-gate 		fdp = &g_rdfd;
275*7c478bd9Sstevel@tonic-gate 		oflags = O_RDONLY;
276*7c478bd9Sstevel@tonic-gate 		file = "/dev/null";
277*7c478bd9Sstevel@tonic-gate 	} else {
278*7c478bd9Sstevel@tonic-gate 		fdp = &g_wrfd;
279*7c478bd9Sstevel@tonic-gate 		oflags = O_RDWR | O_APPEND;
280*7c478bd9Sstevel@tonic-gate 		file = &nout[0];
281*7c478bd9Sstevel@tonic-gate 	}
282*7c478bd9Sstevel@tonic-gate 
283*7c478bd9Sstevel@tonic-gate 	if (*fdp < 0) {
284*7c478bd9Sstevel@tonic-gate 		(void) pr_close(g_proc, fd);
285*7c478bd9Sstevel@tonic-gate 
286*7c478bd9Sstevel@tonic-gate 		tmpfd = pr_open(g_proc, file, oflags, 0);
287*7c478bd9Sstevel@tonic-gate 
288*7c478bd9Sstevel@tonic-gate 		if (tmpfd < 0) {
289*7c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
290*7c478bd9Sstevel@tonic-gate 			    gettext("nohup: process %d cannot open %s: %s\n"),
291*7c478bd9Sstevel@tonic-gate 			    Pstatus(g_proc)->pr_pid, file, strerror(errno));
292*7c478bd9Sstevel@tonic-gate 
293*7c478bd9Sstevel@tonic-gate 			goto err;
294*7c478bd9Sstevel@tonic-gate 		}
295*7c478bd9Sstevel@tonic-gate 
296*7c478bd9Sstevel@tonic-gate 		if (tmpfd != fd) {
297*7c478bd9Sstevel@tonic-gate 			(void) pr_fcntl(g_proc, tmpfd, F_DUP2FD,
298*7c478bd9Sstevel@tonic-gate 				(void *)(uintptr_t)fd);
299*7c478bd9Sstevel@tonic-gate 			(void) pr_close(g_proc, tmpfd);
300*7c478bd9Sstevel@tonic-gate 		}
301*7c478bd9Sstevel@tonic-gate 
302*7c478bd9Sstevel@tonic-gate 		*fdp = fd;
303*7c478bd9Sstevel@tonic-gate 	} else {
304*7c478bd9Sstevel@tonic-gate 		(void) pr_fcntl(g_proc, *fdp, F_DUP2FD, (void *)(uintptr_t)fd);
305*7c478bd9Sstevel@tonic-gate 	}
306*7c478bd9Sstevel@tonic-gate 
307*7c478bd9Sstevel@tonic-gate 	return (0);
308*7c478bd9Sstevel@tonic-gate 
309*7c478bd9Sstevel@tonic-gate err:
310*7c478bd9Sstevel@tonic-gate 	/*
311*7c478bd9Sstevel@tonic-gate 	 * The victim couldn't open nohup.out so we'll have it try to reopen
312*7c478bd9Sstevel@tonic-gate 	 * its terminal. If this fails, we are left with little recourse.
313*7c478bd9Sstevel@tonic-gate 	 */
314*7c478bd9Sstevel@tonic-gate 	tmpfd = pr_open(g_proc, "/dev/tty", O_RDWR, 0);
315*7c478bd9Sstevel@tonic-gate 
316*7c478bd9Sstevel@tonic-gate 	if (tmpfd != fd && tmpfd >= 0) {
317*7c478bd9Sstevel@tonic-gate 		(void) pr_fcntl(g_proc, tmpfd, F_DUP2FD, (void *)(uintptr_t)fd);
318*7c478bd9Sstevel@tonic-gate 		(void) pr_close(g_proc, tmpfd);
319*7c478bd9Sstevel@tonic-gate 	}
320*7c478bd9Sstevel@tonic-gate 
321*7c478bd9Sstevel@tonic-gate 	return (1);
322*7c478bd9Sstevel@tonic-gate }
323*7c478bd9Sstevel@tonic-gate 
324*7c478bd9Sstevel@tonic-gate static int
lwp_restartable(short syscall)325*7c478bd9Sstevel@tonic-gate lwp_restartable(short syscall)
326*7c478bd9Sstevel@tonic-gate {
327*7c478bd9Sstevel@tonic-gate 	switch (syscall) {
328*7c478bd9Sstevel@tonic-gate 	case SYS_read:
329*7c478bd9Sstevel@tonic-gate 	case SYS_readv:
330*7c478bd9Sstevel@tonic-gate 	case SYS_pread:
331*7c478bd9Sstevel@tonic-gate 	case SYS_pread64:
332*7c478bd9Sstevel@tonic-gate 	case SYS_write:
333*7c478bd9Sstevel@tonic-gate 	case SYS_writev:
334*7c478bd9Sstevel@tonic-gate 	case SYS_pwrite:
335*7c478bd9Sstevel@tonic-gate 	case SYS_pwrite64:
336*7c478bd9Sstevel@tonic-gate 	case SYS_ioctl:
337*7c478bd9Sstevel@tonic-gate 	case SYS_fcntl:
338*7c478bd9Sstevel@tonic-gate 	case SYS_getmsg:
339*7c478bd9Sstevel@tonic-gate 	case SYS_getpmsg:
340*7c478bd9Sstevel@tonic-gate 	case SYS_putmsg:
341*7c478bd9Sstevel@tonic-gate 	case SYS_putpmsg:
342*7c478bd9Sstevel@tonic-gate 	case SYS_recv:
343*7c478bd9Sstevel@tonic-gate 	case SYS_recvmsg:
344*7c478bd9Sstevel@tonic-gate 	case SYS_recvfrom:
345*7c478bd9Sstevel@tonic-gate 	case SYS_send:
346*7c478bd9Sstevel@tonic-gate 	case SYS_sendmsg:
347*7c478bd9Sstevel@tonic-gate 	case SYS_sendto:
348*7c478bd9Sstevel@tonic-gate 		return (1);
349*7c478bd9Sstevel@tonic-gate 	}
350*7c478bd9Sstevel@tonic-gate 
351*7c478bd9Sstevel@tonic-gate 	return (0);
352*7c478bd9Sstevel@tonic-gate }
353*7c478bd9Sstevel@tonic-gate 
354*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
355*7c478bd9Sstevel@tonic-gate static int
lwp_abort(void * data,const lwpstatus_t * lsp)356*7c478bd9Sstevel@tonic-gate lwp_abort(void *data, const lwpstatus_t *lsp)
357*7c478bd9Sstevel@tonic-gate {
358*7c478bd9Sstevel@tonic-gate 	struct ps_lwphandle *L;
359*7c478bd9Sstevel@tonic-gate 	int err;
360*7c478bd9Sstevel@tonic-gate 
361*7c478bd9Sstevel@tonic-gate 	/*
362*7c478bd9Sstevel@tonic-gate 	 * Continue if this lwp isn't asleep in a restartable syscall.
363*7c478bd9Sstevel@tonic-gate 	 */
364*7c478bd9Sstevel@tonic-gate 	if (!(lsp->pr_flags & PR_ASLEEP) || !lwp_restartable(lsp->pr_syscall))
365*7c478bd9Sstevel@tonic-gate 		return (0);
366*7c478bd9Sstevel@tonic-gate 
367*7c478bd9Sstevel@tonic-gate 	L = Lgrab(g_proc, lsp->pr_lwpid, &err);
368*7c478bd9Sstevel@tonic-gate 	(void) Lsetrun(L, 0, PRSABORT);
369*7c478bd9Sstevel@tonic-gate 	Lfree(L);
370*7c478bd9Sstevel@tonic-gate 
371*7c478bd9Sstevel@tonic-gate 	/*
372*7c478bd9Sstevel@tonic-gate 	 * Indicate that we have aborted a syscall.
373*7c478bd9Sstevel@tonic-gate 	 */
374*7c478bd9Sstevel@tonic-gate 	g_dirty = 1;
375*7c478bd9Sstevel@tonic-gate 
376*7c478bd9Sstevel@tonic-gate 	return (0);
377*7c478bd9Sstevel@tonic-gate }
378*7c478bd9Sstevel@tonic-gate 
379*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
380*7c478bd9Sstevel@tonic-gate static int
lwp_restart(void * data,const lwpstatus_t * lsp)381*7c478bd9Sstevel@tonic-gate lwp_restart(void *data, const lwpstatus_t *lsp)
382*7c478bd9Sstevel@tonic-gate {
383*7c478bd9Sstevel@tonic-gate 	struct ps_lwphandle *L;
384*7c478bd9Sstevel@tonic-gate 	int err;
385*7c478bd9Sstevel@tonic-gate 
386*7c478bd9Sstevel@tonic-gate 	/*
387*7c478bd9Sstevel@tonic-gate 	 * If any lwp is still sleeping in a restartable syscall, it means
388*7c478bd9Sstevel@tonic-gate 	 * the lwp is wedged and we've screwed up.
389*7c478bd9Sstevel@tonic-gate 	 */
390*7c478bd9Sstevel@tonic-gate 	if (lsp->pr_flags & PR_ASLEEP) {
391*7c478bd9Sstevel@tonic-gate 		if (!lwp_restartable(lsp->pr_syscall))
392*7c478bd9Sstevel@tonic-gate 			return (0);
393*7c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, gettext("nohup: LWP %d failed "
394*7c478bd9Sstevel@tonic-gate 		    "to abort syscall (%d) in process %d\n"),
395*7c478bd9Sstevel@tonic-gate 		    lsp->pr_lwpid, lsp->pr_syscall, Pstatus(g_proc)->pr_pid);
396*7c478bd9Sstevel@tonic-gate 		return (1);
397*7c478bd9Sstevel@tonic-gate 	}
398*7c478bd9Sstevel@tonic-gate 
399*7c478bd9Sstevel@tonic-gate 	if (lsp->pr_why == PR_SYSEXIT && lsp->pr_errno == EINTR) {
400*7c478bd9Sstevel@tonic-gate 		L = Lgrab(g_proc, lsp->pr_lwpid, &err);
401*7c478bd9Sstevel@tonic-gate 		(void) Lputareg(L, R_R0, ERESTART);
402*7c478bd9Sstevel@tonic-gate 		Lsync(L);
403*7c478bd9Sstevel@tonic-gate 		Lfree(L);
404*7c478bd9Sstevel@tonic-gate 	}
405*7c478bd9Sstevel@tonic-gate 
406*7c478bd9Sstevel@tonic-gate 	return (0);
407*7c478bd9Sstevel@tonic-gate }
408*7c478bd9Sstevel@tonic-gate 
409*7c478bd9Sstevel@tonic-gate static int
do_pnohup(struct ps_prochandle * P)410*7c478bd9Sstevel@tonic-gate do_pnohup(struct ps_prochandle *P)
411*7c478bd9Sstevel@tonic-gate {
412*7c478bd9Sstevel@tonic-gate 	int sig = 0;
413*7c478bd9Sstevel@tonic-gate 	struct sigaction sa;
414*7c478bd9Sstevel@tonic-gate 	const pstatus_t *psp;
415*7c478bd9Sstevel@tonic-gate 
416*7c478bd9Sstevel@tonic-gate 	psp = Pstatus(P);
417*7c478bd9Sstevel@tonic-gate 
418*7c478bd9Sstevel@tonic-gate 	/*
419*7c478bd9Sstevel@tonic-gate 	 * Make sure there's a pending procfs stop directive.
420*7c478bd9Sstevel@tonic-gate 	 */
421*7c478bd9Sstevel@tonic-gate 	(void) Pdstop(P);
422*7c478bd9Sstevel@tonic-gate 
423*7c478bd9Sstevel@tonic-gate 	if (Pcreate_agent(P) != 0) {
424*7c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, gettext("nohup: cannot control "
425*7c478bd9Sstevel@tonic-gate 		    "process %d\n"), psp->pr_pid);
426*7c478bd9Sstevel@tonic-gate 		goto err_no_agent;
427*7c478bd9Sstevel@tonic-gate 	}
428*7c478bd9Sstevel@tonic-gate 
429*7c478bd9Sstevel@tonic-gate 	/*
430*7c478bd9Sstevel@tonic-gate 	 * Set the disposition of SIGHUP and SIGQUIT to SIG_IGN. If either
431*7c478bd9Sstevel@tonic-gate 	 * signal is handled by the victim, only adjust the disposition if
432*7c478bd9Sstevel@tonic-gate 	 * the -a flag is set.
433*7c478bd9Sstevel@tonic-gate 	 */
434*7c478bd9Sstevel@tonic-gate 	if (!opt_a && pr_sigaction(P, SIGHUP, NULL, &sa) != 0) {
435*7c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, gettext("nohup: cannot read "
436*7c478bd9Sstevel@tonic-gate 		    "disposition of SIGHUP for %d\n"), psp->pr_pid);
437*7c478bd9Sstevel@tonic-gate 		goto no_sigs;
438*7c478bd9Sstevel@tonic-gate 	}
439*7c478bd9Sstevel@tonic-gate 
440*7c478bd9Sstevel@tonic-gate 	if (!opt_a && sa.sa_handler != SIG_DFL && sa.sa_handler != SIG_IGN) {
441*7c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, gettext("nohup: SIGHUP already handled "
442*7c478bd9Sstevel@tonic-gate 		    "by %d; use -a to force process to ignore\n"), psp->pr_pid);
443*7c478bd9Sstevel@tonic-gate 		goto no_sigs;
444*7c478bd9Sstevel@tonic-gate 	}
445*7c478bd9Sstevel@tonic-gate 
446*7c478bd9Sstevel@tonic-gate 	if (!opt_a && pr_sigaction(P, SIGQUIT, NULL, &sa) != 0) {
447*7c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, gettext("nohup: cannot read "
448*7c478bd9Sstevel@tonic-gate 		    "disposition of SIGQUIT for %d\n"), psp->pr_pid);
449*7c478bd9Sstevel@tonic-gate 		goto no_sigs;
450*7c478bd9Sstevel@tonic-gate 	}
451*7c478bd9Sstevel@tonic-gate 
452*7c478bd9Sstevel@tonic-gate 	if (!opt_a && sa.sa_handler != SIG_DFL && sa.sa_handler != SIG_IGN) {
453*7c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, gettext("nohup: SIGQUIT already handled "
454*7c478bd9Sstevel@tonic-gate 		    "by %d; use -a to force process to ignore\n"), psp->pr_pid);
455*7c478bd9Sstevel@tonic-gate 		goto no_sigs;
456*7c478bd9Sstevel@tonic-gate 	}
457*7c478bd9Sstevel@tonic-gate 
458*7c478bd9Sstevel@tonic-gate 	sa.sa_handler = SIG_IGN;
459*7c478bd9Sstevel@tonic-gate 
460*7c478bd9Sstevel@tonic-gate 	if (pr_sigaction(P, SIGHUP, &sa, NULL) != 0) {
461*7c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, gettext("nohup: cannot set "
462*7c478bd9Sstevel@tonic-gate 		    "disposition of SIGHUP for %d\n"), psp->pr_pid);
463*7c478bd9Sstevel@tonic-gate 		goto no_sigs;
464*7c478bd9Sstevel@tonic-gate 	}
465*7c478bd9Sstevel@tonic-gate 
466*7c478bd9Sstevel@tonic-gate 	if (pr_sigaction(P, SIGQUIT, &sa, NULL) != 0) {
467*7c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, gettext("nohup: cannot set "
468*7c478bd9Sstevel@tonic-gate 		    "disposition of SIGQUIT for %d\n"), psp->pr_pid);
469*7c478bd9Sstevel@tonic-gate 		goto no_sigs;
470*7c478bd9Sstevel@tonic-gate 	}
471*7c478bd9Sstevel@tonic-gate 
472*7c478bd9Sstevel@tonic-gate no_sigs:
473*7c478bd9Sstevel@tonic-gate 	Pdestroy_agent(P);
474*7c478bd9Sstevel@tonic-gate 
475*7c478bd9Sstevel@tonic-gate 	/*
476*7c478bd9Sstevel@tonic-gate 	 * We need to close and reassign some file descriptors, but we
477*7c478bd9Sstevel@tonic-gate 	 * need to be careful about how we do it. If we send in the agent
478*7c478bd9Sstevel@tonic-gate 	 * to close some fd and there's an lwp asleep in the kernel due to
479*7c478bd9Sstevel@tonic-gate 	 * a syscall using that fd, then we have a problem. The normal
480*7c478bd9Sstevel@tonic-gate 	 * sequence of events is the close syscall wakes up any threads
481*7c478bd9Sstevel@tonic-gate 	 * that have the fd in question active (see kthread.t_activefd)
482*7c478bd9Sstevel@tonic-gate 	 * and then waits for those threads to wake up and release the
483*7c478bd9Sstevel@tonic-gate 	 * file descriptors (they then continue to user-land to return
484*7c478bd9Sstevel@tonic-gate 	 * EBADF from the syscall). However, recall that if the agent lwp
485*7c478bd9Sstevel@tonic-gate 	 * is present in a process, no other lwps can run, so if the agent
486*7c478bd9Sstevel@tonic-gate 	 * lwp itself is making the call to close(2) (or something else
487*7c478bd9Sstevel@tonic-gate 	 * like dup2 that involves a call to closeandsetf()) then we're in
488*7c478bd9Sstevel@tonic-gate 	 * pretty bad shape. The solution is to abort and restart any lwp
489*7c478bd9Sstevel@tonic-gate 	 * asleep in a syscall on the off chance that it may be using one
490*7c478bd9Sstevel@tonic-gate 	 * of the file descriptors that we want to manipulate.
491*7c478bd9Sstevel@tonic-gate 	 */
492*7c478bd9Sstevel@tonic-gate 
493*7c478bd9Sstevel@tonic-gate 	/*
494*7c478bd9Sstevel@tonic-gate 	 * We may need to chase some lwps out of the kernel briefly, so we
495*7c478bd9Sstevel@tonic-gate 	 * send SIGCONT to the process if it was previously stopped due to
496*7c478bd9Sstevel@tonic-gate 	 * a job control signal, and save the current signal to repost it
497*7c478bd9Sstevel@tonic-gate 	 * when we detatch from the victim. A process that is stopped due
498*7c478bd9Sstevel@tonic-gate 	 * to job control will start running as soon as we send SIGCONT
499*7c478bd9Sstevel@tonic-gate 	 * since there is no procfs stop command pending; we use Pdstop to
500*7c478bd9Sstevel@tonic-gate 	 * post a procfs stop request (above).
501*7c478bd9Sstevel@tonic-gate 	 */
502*7c478bd9Sstevel@tonic-gate 	if ((psp->pr_lwp.pr_flags & PR_STOPPED) &&
503*7c478bd9Sstevel@tonic-gate 	    psp->pr_lwp.pr_why == PR_JOBCONTROL) {
504*7c478bd9Sstevel@tonic-gate 		sig = psp->pr_lwp.pr_what;
505*7c478bd9Sstevel@tonic-gate 		(void) kill(psp->pr_pid, SIGCONT);
506*7c478bd9Sstevel@tonic-gate 		(void) Pwait(P, 0);
507*7c478bd9Sstevel@tonic-gate 	}
508*7c478bd9Sstevel@tonic-gate 
509*7c478bd9Sstevel@tonic-gate 	(void) Psysexit(P, 0, 1);
510*7c478bd9Sstevel@tonic-gate 
511*7c478bd9Sstevel@tonic-gate 	/*
512*7c478bd9Sstevel@tonic-gate 	 * Abort each syscall; set g_dirty if any lwp was asleep.
513*7c478bd9Sstevel@tonic-gate 	 */
514*7c478bd9Sstevel@tonic-gate 	g_dirty = 0;
515*7c478bd9Sstevel@tonic-gate 	g_proc = P;
516*7c478bd9Sstevel@tonic-gate 	(void) Plwp_iter(P, lwp_abort, NULL);
517*7c478bd9Sstevel@tonic-gate 
518*7c478bd9Sstevel@tonic-gate 	if (g_dirty) {
519*7c478bd9Sstevel@tonic-gate 		/*
520*7c478bd9Sstevel@tonic-gate 		 * Block until each lwp that was asleep in a syscall has
521*7c478bd9Sstevel@tonic-gate 		 * wandered back up to user-land.
522*7c478bd9Sstevel@tonic-gate 		 */
523*7c478bd9Sstevel@tonic-gate 		(void) Pwait(P, 0);
524*7c478bd9Sstevel@tonic-gate 
525*7c478bd9Sstevel@tonic-gate 		/*
526*7c478bd9Sstevel@tonic-gate 		 * Make sure that each lwp has successfully aborted its
527*7c478bd9Sstevel@tonic-gate 		 * syscall and that the syscall gets restarted when we
528*7c478bd9Sstevel@tonic-gate 		 * detach later.
529*7c478bd9Sstevel@tonic-gate 		 */
530*7c478bd9Sstevel@tonic-gate 		if (Plwp_iter(P, lwp_restart, NULL) != 0)
531*7c478bd9Sstevel@tonic-gate 			goto err_no_agent;
532*7c478bd9Sstevel@tonic-gate 	}
533*7c478bd9Sstevel@tonic-gate 
534*7c478bd9Sstevel@tonic-gate 	(void) Psysexit(P, 0, 0);
535*7c478bd9Sstevel@tonic-gate 
536*7c478bd9Sstevel@tonic-gate 	if (Pcreate_agent(P) != 0) {
537*7c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, gettext("nohup: cannot control "
538*7c478bd9Sstevel@tonic-gate 		    "process %d\n"), psp->pr_pid);
539*7c478bd9Sstevel@tonic-gate 		goto err_no_agent;
540*7c478bd9Sstevel@tonic-gate 	}
541*7c478bd9Sstevel@tonic-gate 
542*7c478bd9Sstevel@tonic-gate 	/*
543*7c478bd9Sstevel@tonic-gate 	 * See if the victim has access to the nohup.out file we created.
544*7c478bd9Sstevel@tonic-gate 	 * If the user does something that would invalidate the result
545*7c478bd9Sstevel@tonic-gate 	 * of this call from here until the call to pr_open, the process
546*7c478bd9Sstevel@tonic-gate 	 * may be left in an inconsistent state -- we assume that the user
547*7c478bd9Sstevel@tonic-gate 	 * is not intentionally trying to shoot himself in the foot.
548*7c478bd9Sstevel@tonic-gate 	 */
549*7c478bd9Sstevel@tonic-gate 	if (pr_access(P, nout, R_OK | W_OK) != 0) {
550*7c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, gettext("nohup: process %d can not "
551*7c478bd9Sstevel@tonic-gate 		    "access %s: %s\n"), psp->pr_pid, nout, strerror(errno));
552*7c478bd9Sstevel@tonic-gate 		goto err_agent;
553*7c478bd9Sstevel@tonic-gate 	}
554*7c478bd9Sstevel@tonic-gate 
555*7c478bd9Sstevel@tonic-gate 	/*
556*7c478bd9Sstevel@tonic-gate 	 * Redirect output to the controlling tty to nohup.out and tty
557*7c478bd9Sstevel@tonic-gate 	 * input to read from /dev/null.
558*7c478bd9Sstevel@tonic-gate 	 */
559*7c478bd9Sstevel@tonic-gate 
560*7c478bd9Sstevel@tonic-gate 	g_wrfd = -1;
561*7c478bd9Sstevel@tonic-gate 	g_rdfd = -1;
562*7c478bd9Sstevel@tonic-gate 
563*7c478bd9Sstevel@tonic-gate 	(void) Pfd_iter(P, fd_cb, NULL);
564*7c478bd9Sstevel@tonic-gate 
565*7c478bd9Sstevel@tonic-gate 	Pdestroy_agent(P);
566*7c478bd9Sstevel@tonic-gate 	if (sig != 0)
567*7c478bd9Sstevel@tonic-gate 		(void) kill(psp->pr_pid, sig);
568*7c478bd9Sstevel@tonic-gate 
569*7c478bd9Sstevel@tonic-gate 	return (0);
570*7c478bd9Sstevel@tonic-gate 
571*7c478bd9Sstevel@tonic-gate err_agent:
572*7c478bd9Sstevel@tonic-gate 	Pdestroy_agent(P);
573*7c478bd9Sstevel@tonic-gate err_no_agent:
574*7c478bd9Sstevel@tonic-gate 	if (sig != 0)
575*7c478bd9Sstevel@tonic-gate 		(void) kill(psp->pr_pid, sig);
576*7c478bd9Sstevel@tonic-gate 	return (-1);
577*7c478bd9Sstevel@tonic-gate }
578*7c478bd9Sstevel@tonic-gate 
579*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
580*7c478bd9Sstevel@tonic-gate static void
intr(int sig)581*7c478bd9Sstevel@tonic-gate intr(int sig)
582*7c478bd9Sstevel@tonic-gate {
583*7c478bd9Sstevel@tonic-gate 	g_interrupt = 1;
584*7c478bd9Sstevel@tonic-gate }
585*7c478bd9Sstevel@tonic-gate 
586*7c478bd9Sstevel@tonic-gate static int
pnohup(int argc,char ** argv)587*7c478bd9Sstevel@tonic-gate pnohup(int argc, char **argv)
588*7c478bd9Sstevel@tonic-gate {
589*7c478bd9Sstevel@tonic-gate 	struct ps_prochandle *P;
590*7c478bd9Sstevel@tonic-gate 	int i, j;
591*7c478bd9Sstevel@tonic-gate 	int flag = 0;
592*7c478bd9Sstevel@tonic-gate 	int gcode;
593*7c478bd9Sstevel@tonic-gate 	int nh_fd = -1;
594*7c478bd9Sstevel@tonic-gate 	char *fname;
595*7c478bd9Sstevel@tonic-gate 	char *home;
596*7c478bd9Sstevel@tonic-gate 	int nerrs = 0;
597*7c478bd9Sstevel@tonic-gate 
598*7c478bd9Sstevel@tonic-gate 	/*
599*7c478bd9Sstevel@tonic-gate 	 * Catch signals from the terminal.
600*7c478bd9Sstevel@tonic-gate 	 */
601*7c478bd9Sstevel@tonic-gate 	if (sigset(SIGHUP, SIG_IGN) == SIG_DFL)
602*7c478bd9Sstevel@tonic-gate 		(void) sigset(SIGHUP, intr);
603*7c478bd9Sstevel@tonic-gate 	if (sigset(SIGINT, SIG_IGN) == SIG_DFL)
604*7c478bd9Sstevel@tonic-gate 		(void) sigset(SIGINT, intr);
605*7c478bd9Sstevel@tonic-gate 	if (sigset(SIGQUIT, SIG_IGN) == SIG_DFL)
606*7c478bd9Sstevel@tonic-gate 		(void) sigset(SIGQUIT, intr);
607*7c478bd9Sstevel@tonic-gate 	(void) sigset(SIGPIPE, intr);
608*7c478bd9Sstevel@tonic-gate 	(void) sigset(SIGTERM, intr);
609*7c478bd9Sstevel@tonic-gate 
610*7c478bd9Sstevel@tonic-gate 	if (opt_F)
611*7c478bd9Sstevel@tonic-gate 		flag |= PGRAB_FORCE;
612*7c478bd9Sstevel@tonic-gate 
613*7c478bd9Sstevel@tonic-gate 	/*
614*7c478bd9Sstevel@tonic-gate 	 * Set nout to be the full path name of nohup.out and fname to be
615*7c478bd9Sstevel@tonic-gate 	 * the simplified path name:
616*7c478bd9Sstevel@tonic-gate 	 * nout = /cwd/nohup.out	fname = nohup.out
617*7c478bd9Sstevel@tonic-gate 	 * nout = $HOME/nohup.out	fname = $HOME/nohup.out
618*7c478bd9Sstevel@tonic-gate 	 */
619*7c478bd9Sstevel@tonic-gate 	if (getcwd(nout, sizeof (nout) - strlen("/nohup.out") - 1) != NULL) {
620*7c478bd9Sstevel@tonic-gate 		fname = &nout[strlen(nout)];
621*7c478bd9Sstevel@tonic-gate 		(void) strcpy(fname, "/nohup.out");
622*7c478bd9Sstevel@tonic-gate 		fname++;
623*7c478bd9Sstevel@tonic-gate 
624*7c478bd9Sstevel@tonic-gate 		nh_fd = open(nout, O_WRONLY | O_CREAT, NOHUP_PERM);
625*7c478bd9Sstevel@tonic-gate 	}
626*7c478bd9Sstevel@tonic-gate 
627*7c478bd9Sstevel@tonic-gate 	if (nh_fd == -1 && (home = getenv("HOME")) != NULL) {
628*7c478bd9Sstevel@tonic-gate 		if (snprintf(nout, sizeof (nout),
629*7c478bd9Sstevel@tonic-gate 		    "%s/nohup.out", home) < sizeof (nout)) {
630*7c478bd9Sstevel@tonic-gate 			nh_fd = open(nout, O_WRONLY | O_CREAT, NOHUP_PERM);
631*7c478bd9Sstevel@tonic-gate 			fname = &nout[0];
632*7c478bd9Sstevel@tonic-gate 		}
633*7c478bd9Sstevel@tonic-gate 	}
634*7c478bd9Sstevel@tonic-gate 
635*7c478bd9Sstevel@tonic-gate 	if (nh_fd == -1) {
636*7c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, gettext("nohup: cannot open/create "
637*7c478bd9Sstevel@tonic-gate 		    "nohup.out: %s\n"), strerror(errno));
638*7c478bd9Sstevel@tonic-gate 
639*7c478bd9Sstevel@tonic-gate 		return (NOHUP_ERROR);
640*7c478bd9Sstevel@tonic-gate 	}
641*7c478bd9Sstevel@tonic-gate 
642*7c478bd9Sstevel@tonic-gate 	if (opt_g) {
643*7c478bd9Sstevel@tonic-gate 		pid_t *pgids;
644*7c478bd9Sstevel@tonic-gate 		int npgids;
645*7c478bd9Sstevel@tonic-gate 		int success;
646*7c478bd9Sstevel@tonic-gate 
647*7c478bd9Sstevel@tonic-gate 		/*
648*7c478bd9Sstevel@tonic-gate 		 * Make nohup its own process group leader so that we
649*7c478bd9Sstevel@tonic-gate 		 * don't accidently send SIGSTOP to this process.
650*7c478bd9Sstevel@tonic-gate 		 */
651*7c478bd9Sstevel@tonic-gate 		(void) setpgid(0, 0);
652*7c478bd9Sstevel@tonic-gate 
653*7c478bd9Sstevel@tonic-gate 		/*
654*7c478bd9Sstevel@tonic-gate 		 * If a list of process group ids is specified, we want to
655*7c478bd9Sstevel@tonic-gate 		 * first SIGSTOP the whole process group so that we can be
656*7c478bd9Sstevel@tonic-gate 		 * sure not to miss any processes that belong to the group
657*7c478bd9Sstevel@tonic-gate 		 * (it's harder to hit a moving target). We then iterate
658*7c478bd9Sstevel@tonic-gate 		 * over all the processes on the system looking for
659*7c478bd9Sstevel@tonic-gate 		 * members of the given process group to apply the
660*7c478bd9Sstevel@tonic-gate 		 * do_pnohup function to. If the process was stopped due
661*7c478bd9Sstevel@tonic-gate 		 * to our SIGSTOP, we send the process SIGCONT; if the
662*7c478bd9Sstevel@tonic-gate 		 * process was already stopped, we leave it alone.
663*7c478bd9Sstevel@tonic-gate 		 */
664*7c478bd9Sstevel@tonic-gate 		pgids = calloc(argc, sizeof (pid_t));
665*7c478bd9Sstevel@tonic-gate 		pgids[0] = getpid();
666*7c478bd9Sstevel@tonic-gate 		npgids = 1;
667*7c478bd9Sstevel@tonic-gate 
668*7c478bd9Sstevel@tonic-gate 		for (i = 0; i < argc; i++) {
669*7c478bd9Sstevel@tonic-gate 			dirent_t *dent;
670*7c478bd9Sstevel@tonic-gate 			DIR *dirp;
671*7c478bd9Sstevel@tonic-gate 			psinfo_t psinfo;
672*7c478bd9Sstevel@tonic-gate 			const pstatus_t *psp;
673*7c478bd9Sstevel@tonic-gate 			pid_t pgid;
674*7c478bd9Sstevel@tonic-gate 			char *end;
675*7c478bd9Sstevel@tonic-gate 			hrtime_t kill_time, stop_time;
676*7c478bd9Sstevel@tonic-gate 
677*7c478bd9Sstevel@tonic-gate 			if (isdigit(*argv[i])) {
678*7c478bd9Sstevel@tonic-gate 				pgid = strtol(argv[i], &end, 10);
679*7c478bd9Sstevel@tonic-gate 
680*7c478bd9Sstevel@tonic-gate 				/*
681*7c478bd9Sstevel@tonic-gate 				 * kill(2) with pid = 0 or -1 has a special
682*7c478bd9Sstevel@tonic-gate 				 * meaning, so don't let pgid be 0 or 1.
683*7c478bd9Sstevel@tonic-gate 				 */
684*7c478bd9Sstevel@tonic-gate 				if (*end == '\0' && pgid > 1)
685*7c478bd9Sstevel@tonic-gate 					goto pgid_ok;
686*7c478bd9Sstevel@tonic-gate 			}
687*7c478bd9Sstevel@tonic-gate 
688*7c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr, gettext("nohup: "
689*7c478bd9Sstevel@tonic-gate 			    "bad process group %s\n"), argv[i]);
690*7c478bd9Sstevel@tonic-gate 			nerrs++;
691*7c478bd9Sstevel@tonic-gate 			continue;
692*7c478bd9Sstevel@tonic-gate 
693*7c478bd9Sstevel@tonic-gate pgid_ok:
694*7c478bd9Sstevel@tonic-gate 			/*
695*7c478bd9Sstevel@tonic-gate 			 * We don't want to nohup a process group twice.
696*7c478bd9Sstevel@tonic-gate 			 */
697*7c478bd9Sstevel@tonic-gate 			for (j = 0; j < npgids; j++) {
698*7c478bd9Sstevel@tonic-gate 				if (pgids[j] == pgid)
699*7c478bd9Sstevel@tonic-gate 					break;
700*7c478bd9Sstevel@tonic-gate 			}
701*7c478bd9Sstevel@tonic-gate 
702*7c478bd9Sstevel@tonic-gate 			if (j != npgids)
703*7c478bd9Sstevel@tonic-gate 				continue;
704*7c478bd9Sstevel@tonic-gate 
705*7c478bd9Sstevel@tonic-gate 			pgids[npgids++] = pgid;
706*7c478bd9Sstevel@tonic-gate 
707*7c478bd9Sstevel@tonic-gate 			/*
708*7c478bd9Sstevel@tonic-gate 			 * Have the kernel stop all members of the process
709*7c478bd9Sstevel@tonic-gate 			 * group; record the time we stopped the process
710*7c478bd9Sstevel@tonic-gate 			 * group so that we can tell if a member stopped
711*7c478bd9Sstevel@tonic-gate 			 * because of this call to kill(2) or if it was
712*7c478bd9Sstevel@tonic-gate 			 * already stopped when we got here. If the user
713*7c478bd9Sstevel@tonic-gate 			 * job control stops the victim between the call
714*7c478bd9Sstevel@tonic-gate 			 * to gethrtime(2) and kill(2), we may send
715*7c478bd9Sstevel@tonic-gate 			 * SIGCONT when we really shouldn't -- we assume
716*7c478bd9Sstevel@tonic-gate 			 * that the user is not trying to shoot himself in
717*7c478bd9Sstevel@tonic-gate 			 * the foot.
718*7c478bd9Sstevel@tonic-gate 			 */
719*7c478bd9Sstevel@tonic-gate 			kill_time = gethrtime();
720*7c478bd9Sstevel@tonic-gate 			if (kill(-pgid, SIGSTOP) == -1) {
721*7c478bd9Sstevel@tonic-gate 				(void) fprintf(stderr, gettext("nohup: cannot "
722*7c478bd9Sstevel@tonic-gate 				    "stop process group %d: %s\n"), pgid,
723*7c478bd9Sstevel@tonic-gate 				    errno != ESRCH ? strerror(errno) :
724*7c478bd9Sstevel@tonic-gate 				    gettext("No such process group"));
725*7c478bd9Sstevel@tonic-gate 
726*7c478bd9Sstevel@tonic-gate 				nerrs++;
727*7c478bd9Sstevel@tonic-gate 				continue;
728*7c478bd9Sstevel@tonic-gate 			}
729*7c478bd9Sstevel@tonic-gate 
730*7c478bd9Sstevel@tonic-gate 			dirp = opendir("/proc");
731*7c478bd9Sstevel@tonic-gate 			success = 0;
732*7c478bd9Sstevel@tonic-gate 			while ((dent = readdir(dirp)) != NULL && !g_interrupt) {
733*7c478bd9Sstevel@tonic-gate 				if (dent->d_name[0] == '.')
734*7c478bd9Sstevel@tonic-gate 					continue;
735*7c478bd9Sstevel@tonic-gate 
736*7c478bd9Sstevel@tonic-gate 				if (proc_arg_psinfo(dent->d_name,
737*7c478bd9Sstevel@tonic-gate 				    PR_ARG_PIDS, &psinfo, &gcode) == -1)
738*7c478bd9Sstevel@tonic-gate 					continue;
739*7c478bd9Sstevel@tonic-gate 
740*7c478bd9Sstevel@tonic-gate 				if (psinfo.pr_pgid != pgid)
741*7c478bd9Sstevel@tonic-gate 					continue;
742*7c478bd9Sstevel@tonic-gate 
743*7c478bd9Sstevel@tonic-gate 				/*
744*7c478bd9Sstevel@tonic-gate 				 * Ignore zombies.
745*7c478bd9Sstevel@tonic-gate 				 */
746*7c478bd9Sstevel@tonic-gate 				if (psinfo.pr_nlwp == 0)
747*7c478bd9Sstevel@tonic-gate 					continue;
748*7c478bd9Sstevel@tonic-gate 
749*7c478bd9Sstevel@tonic-gate 				if ((P = proc_arg_grab(dent->d_name,
750*7c478bd9Sstevel@tonic-gate 				    PR_ARG_PIDS, flag, &gcode)) == NULL) {
751*7c478bd9Sstevel@tonic-gate 					(void) fprintf(stderr, gettext("nohup: "
752*7c478bd9Sstevel@tonic-gate 					    "cannot examine %s: %s\n"),
753*7c478bd9Sstevel@tonic-gate 					    dent->d_name, Pgrab_error(gcode));
754*7c478bd9Sstevel@tonic-gate 
755*7c478bd9Sstevel@tonic-gate 					(void) kill(psinfo.pr_pid, SIGCONT);
756*7c478bd9Sstevel@tonic-gate 					continue;
757*7c478bd9Sstevel@tonic-gate 				}
758*7c478bd9Sstevel@tonic-gate 
759*7c478bd9Sstevel@tonic-gate 				/*
760*7c478bd9Sstevel@tonic-gate 				 * This implicitly restarts any process that
761*7c478bd9Sstevel@tonic-gate 				 * was stopped via job control any time after
762*7c478bd9Sstevel@tonic-gate 				 * the call to kill(2). This is the desired
763*7c478bd9Sstevel@tonic-gate 				 * behavior since nohup is busy trying to
764*7c478bd9Sstevel@tonic-gate 				 * disassociate a process from its controlling
765*7c478bd9Sstevel@tonic-gate 				 * terminal.
766*7c478bd9Sstevel@tonic-gate 				 */
767*7c478bd9Sstevel@tonic-gate 				psp = Pstatus(P);
768*7c478bd9Sstevel@tonic-gate 				if (psp->pr_lwp.pr_why == PR_JOBCONTROL) {
769*7c478bd9Sstevel@tonic-gate 					stop_time =
770*7c478bd9Sstevel@tonic-gate 					    psp->pr_lwp.pr_tstamp.tv_sec;
771*7c478bd9Sstevel@tonic-gate 					stop_time *= (hrtime_t)NANOSEC;
772*7c478bd9Sstevel@tonic-gate 					stop_time +=
773*7c478bd9Sstevel@tonic-gate 					    psp->pr_lwp.pr_tstamp.tv_nsec;
774*7c478bd9Sstevel@tonic-gate 				} else {
775*7c478bd9Sstevel@tonic-gate 					stop_time = 0;
776*7c478bd9Sstevel@tonic-gate 				}
777*7c478bd9Sstevel@tonic-gate 
778*7c478bd9Sstevel@tonic-gate 				if (do_pnohup(P) == 0)
779*7c478bd9Sstevel@tonic-gate 					success = 1;
780*7c478bd9Sstevel@tonic-gate 
781*7c478bd9Sstevel@tonic-gate 				/*
782*7c478bd9Sstevel@tonic-gate 				 * If the process was stopped because of
783*7c478bd9Sstevel@tonic-gate 				 * our call to kill(2) (i.e. if it stopped
784*7c478bd9Sstevel@tonic-gate 				 * some time after kill_time) then restart
785*7c478bd9Sstevel@tonic-gate 				 * the process.
786*7c478bd9Sstevel@tonic-gate 				 */
787*7c478bd9Sstevel@tonic-gate 				if (kill_time <= stop_time)
788*7c478bd9Sstevel@tonic-gate 					(void) kill(psinfo.pr_pid, SIGCONT);
789*7c478bd9Sstevel@tonic-gate 
790*7c478bd9Sstevel@tonic-gate 				Prelease(P, 0);
791*7c478bd9Sstevel@tonic-gate 			}
792*7c478bd9Sstevel@tonic-gate 
793*7c478bd9Sstevel@tonic-gate 			/*
794*7c478bd9Sstevel@tonic-gate 			 * If we didn't successfully nohup any member of the
795*7c478bd9Sstevel@tonic-gate 			 * process group.
796*7c478bd9Sstevel@tonic-gate 			 */
797*7c478bd9Sstevel@tonic-gate 			if (!success)
798*7c478bd9Sstevel@tonic-gate 				nerrs++;
799*7c478bd9Sstevel@tonic-gate 
800*7c478bd9Sstevel@tonic-gate 			(void) closedir(dirp);
801*7c478bd9Sstevel@tonic-gate 		}
802*7c478bd9Sstevel@tonic-gate 	} else {
803*7c478bd9Sstevel@tonic-gate 		for (i = 0; i < argc && !g_interrupt; i++) {
804*7c478bd9Sstevel@tonic-gate 			if ((P = proc_arg_grab(argv[i], PR_ARG_PIDS, flag,
805*7c478bd9Sstevel@tonic-gate 			    &gcode)) == NULL) {
806*7c478bd9Sstevel@tonic-gate 				(void) fprintf(stderr,
807*7c478bd9Sstevel@tonic-gate 				    gettext("nohup: cannot examine %s: %s\n"),
808*7c478bd9Sstevel@tonic-gate 				    argv[i], Pgrab_error(gcode));
809*7c478bd9Sstevel@tonic-gate 
810*7c478bd9Sstevel@tonic-gate 				nerrs++;
811*7c478bd9Sstevel@tonic-gate 				continue;
812*7c478bd9Sstevel@tonic-gate 			}
813*7c478bd9Sstevel@tonic-gate 
814*7c478bd9Sstevel@tonic-gate 			if (do_pnohup(P) != 0)
815*7c478bd9Sstevel@tonic-gate 				nerrs++;
816*7c478bd9Sstevel@tonic-gate 
817*7c478bd9Sstevel@tonic-gate 			Prelease(P, 0);
818*7c478bd9Sstevel@tonic-gate 		}
819*7c478bd9Sstevel@tonic-gate 	}
820*7c478bd9Sstevel@tonic-gate 
821*7c478bd9Sstevel@tonic-gate 	(void) close(nh_fd);
822*7c478bd9Sstevel@tonic-gate 
823*7c478bd9Sstevel@tonic-gate 	if (argc == nerrs)
824*7c478bd9Sstevel@tonic-gate 		return (NOHUP_ERROR);
825*7c478bd9Sstevel@tonic-gate 
826*7c478bd9Sstevel@tonic-gate 	(void) fprintf(stderr, gettext("Sending output to %s\n"), fname);
827*7c478bd9Sstevel@tonic-gate 
828*7c478bd9Sstevel@tonic-gate 	return (0);
829*7c478bd9Sstevel@tonic-gate }
830*7c478bd9Sstevel@tonic-gate 
831*7c478bd9Sstevel@tonic-gate #endif /* !XPG4 */
832