/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
#pragma ident "%Z%%M% %I% %E% SMI"
#include <stdio.h>
#include <stdlib.h>
#include <nl_types.h>
#include <locale.h>
#include <signal.h>
#include <string.h>
#include <limits.h>
#include <errno.h>
#include <unistd.h>
#include <libproc.h>
#include <dirent.h>
#include <ctype.h>
#ifdef XPG4
#else
static int pnohup(int, char **);
static int g_wrfd;
static int g_rdfd;
static int g_dirty;
static volatile int g_interrupt = 0;
#endif
static int opt_p = 0;
static int opt_g = 0;
static int opt_a = 0;
static int opt_F = 0;
static char *pname;
static int
open_file(void)
{
char *home;
int fd;
return (-1);
return (-1);
}
}
return (fd);
}
int
{
int opt;
int err;
else
#ifndef TEXT_DOMAIN
#endif
(void) textdomain(TEXT_DOMAIN);
switch (opt) {
case 'p':
opt_p = 1;
break;
case 'F':
opt_F = 1;
break;
case 'a':
opt_a = 1;
break;
case 'g':
opt_g = 1;
break;
default:
goto usage;
}
}
if (argc == 0)
goto usage; /* need at least one argument */
#ifndef XPG4
goto usage;
goto usage; /* only valid with -p or -g */
#endif
#ifndef XPG4
#endif
if (isatty(STDOUT_FILENO)) {
goto err;
}
if (isatty(STDERR_FILENO)) {
goto err;
}
if (fd >= 0)
err:
return (NOHUP_ERROR);
#ifdef XPG4
gettext("usage: nohup command [argument ...]\n"));
#else
"\tnohup command [argument ...]\n"
"\tnohup -p [-Fa] pid [pid ...]\n"
"\tnohup -g [-Fa] pgid [pgid ...]\n"));
#endif
return (NOHUP_ERROR);
}
#ifndef XPG4
/*
* File descriptor iteration interface.
*/
typedef int proc_fd_iter_f(void *, int);
static int
{
int ret = 0;
return (-1);
return (-1);
continue;
break;
}
return (ret);
}
/*ARGSUSED*/
static int
{
int flags;
int *fdp;
int oflags;
char *file;
int tmpfd;
/*
* See if this fd refers to the controlling tty.
*/
return (0);
/*
* tty's opened for input are usually O_RDWR so that the program
* can change terminal settings. We assume that if there's a
* controlling tty in the STDIN_FILENO file descriptor that is
* effectively used only for input. If standard in gets dup'ed to
* other file descriptors, then we're out of luck unless the
* program is nice enough to fcntl it to be O_RDONLY. We close the
* file descriptor before we call open to handle the case that
* there are no available file descriptors left in the victim. If
* our call to pr_open fails, we try to reopen the controlling tty.
*/
} else {
}
if (*fdp < 0) {
if (tmpfd < 0) {
gettext("nohup: process %d cannot open %s: %s\n"),
goto err;
}
}
} else {
}
return (0);
err:
/*
* The victim couldn't open nohup.out so we'll have it try to reopen
* its terminal. If this fails, we are left with little recourse.
*/
}
return (1);
}
static int
{
switch (syscall) {
case SYS_read:
case SYS_readv:
case SYS_pread:
case SYS_pread64:
case SYS_write:
case SYS_writev:
case SYS_pwrite:
case SYS_pwrite64:
case SYS_ioctl:
case SYS_fcntl:
case SYS_getmsg:
case SYS_getpmsg:
case SYS_putmsg:
case SYS_putpmsg:
case SYS_recv:
case SYS_recvmsg:
case SYS_recvfrom:
case SYS_send:
case SYS_sendmsg:
case SYS_sendto:
return (1);
}
return (0);
}
/*ARGSUSED*/
static int
{
struct ps_lwphandle *L;
int err;
/*
* Continue if this lwp isn't asleep in a restartable syscall.
*/
return (0);
Lfree(L);
/*
* Indicate that we have aborted a syscall.
*/
g_dirty = 1;
return (0);
}
/*ARGSUSED*/
static int
{
struct ps_lwphandle *L;
int err;
/*
* If any lwp is still sleeping in a restartable syscall, it means
* the lwp is wedged and we've screwed up.
*/
return (0);
"to abort syscall (%d) in process %d\n"),
return (1);
}
Lsync(L);
Lfree(L);
}
return (0);
}
static int
{
int sig = 0;
/*
* Make sure there's a pending procfs stop directive.
*/
(void) Pdstop(P);
if (Pcreate_agent(P) != 0) {
goto err_no_agent;
}
/*
* Set the disposition of SIGHUP and SIGQUIT to SIG_IGN. If either
* signal is handled by the victim, only adjust the disposition if
* the -a flag is set.
*/
goto no_sigs;
}
goto no_sigs;
}
goto no_sigs;
}
goto no_sigs;
}
goto no_sigs;
}
goto no_sigs;
}
Pdestroy_agent(P);
/*
* We need to close and reassign some file descriptors, but we
* need to be careful about how we do it. If we send in the agent
* to close some fd and there's an lwp asleep in the kernel due to
* a syscall using that fd, then we have a problem. The normal
* sequence of events is the close syscall wakes up any threads
* that have the fd in question active (see kthread.t_activefd)
* and then waits for those threads to wake up and release the
* file descriptors (they then continue to user-land to return
* EBADF from the syscall). However, recall that if the agent lwp
* is present in a process, no other lwps can run, so if the agent
* lwp itself is making the call to close(2) (or something else
* like dup2 that involves a call to closeandsetf()) then we're in
* pretty bad shape. The solution is to abort and restart any lwp
* asleep in a syscall on the off chance that it may be using one
* of the file descriptors that we want to manipulate.
*/
/*
* We may need to chase some lwps out of the kernel briefly, so we
* send SIGCONT to the process if it was previously stopped due to
* a job control signal, and save the current signal to repost it
* when we detatch from the victim. A process that is stopped due
* to job control will start running as soon as we send SIGCONT
* since there is no procfs stop command pending; we use Pdstop to
* post a procfs stop request (above).
*/
(void) Pwait(P, 0);
}
(void) Psysexit(P, 0, 1);
/*
* Abort each syscall; set g_dirty if any lwp was asleep.
*/
g_dirty = 0;
g_proc = P;
if (g_dirty) {
/*
* Block until each lwp that was asleep in a syscall has
* wandered back up to user-land.
*/
(void) Pwait(P, 0);
/*
* Make sure that each lwp has successfully aborted its
* syscall and that the syscall gets restarted when we
* detach later.
*/
goto err_no_agent;
}
(void) Psysexit(P, 0, 0);
if (Pcreate_agent(P) != 0) {
goto err_no_agent;
}
/*
* See if the victim has access to the nohup.out file we created.
* If the user does something that would invalidate the result
* of this call from here until the call to pr_open, the process
* may be left in an inconsistent state -- we assume that the user
* is not intentionally trying to shoot himself in the foot.
*/
goto err_agent;
}
/*
* Redirect output to the controlling tty to nohup.out and tty
*/
g_wrfd = -1;
g_rdfd = -1;
Pdestroy_agent(P);
if (sig != 0)
return (0);
Pdestroy_agent(P);
if (sig != 0)
return (-1);
}
/*ARGSUSED*/
static void
{
g_interrupt = 1;
}
static int
{
struct ps_prochandle *P;
int i, j;
int flag = 0;
int gcode;
char *fname;
char *home;
int nerrs = 0;
/*
* Catch signals from the terminal.
*/
if (opt_F)
flag |= PGRAB_FORCE;
/*
* Set nout to be the full path name of nohup.out and fname to be
* the simplified path name:
*/
fname++;
}
}
}
if (nh_fd == -1) {
return (NOHUP_ERROR);
}
if (opt_g) {
int npgids;
int success;
/*
* Make nohup its own process group leader so that we
* don't accidently send SIGSTOP to this process.
*/
(void) setpgid(0, 0);
/*
* If a list of process group ids is specified, we want to
* first SIGSTOP the whole process group so that we can be
* sure not to miss any processes that belong to the group
* (it's harder to hit a moving target). We then iterate
* over all the processes on the system looking for
* members of the given process group to apply the
* do_pnohup function to. If the process was stopped due
* to our SIGSTOP, we send the process SIGCONT; if the
* process was already stopped, we leave it alone.
*/
npgids = 1;
for (i = 0; i < argc; i++) {
char *end;
/*
* kill(2) with pid = 0 or -1 has a special
* meaning, so don't let pgid be 0 or 1.
*/
goto pgid_ok;
}
"bad process group %s\n"), argv[i]);
nerrs++;
continue;
/*
* We don't want to nohup a process group twice.
*/
for (j = 0; j < npgids; j++) {
break;
}
if (j != npgids)
continue;
/*
* Have the kernel stop all members of the process
* group; record the time we stopped the process
* group so that we can tell if a member stopped
* because of this call to kill(2) or if it was
* already stopped when we got here. If the user
* job control stops the victim between the call
* to gethrtime(2) and kill(2), we may send
* SIGCONT when we really shouldn't -- we assume
* that the user is not trying to shoot himself in
* the foot.
*/
"stop process group %d: %s\n"), pgid,
gettext("No such process group"));
nerrs++;
continue;
}
success = 0;
continue;
continue;
continue;
/*
* Ignore zombies.
*/
continue;
"cannot examine %s: %s\n"),
continue;
}
/*
* This implicitly restarts any process that
* was stopped via job control any time after
* the call to kill(2). This is the desired
* behavior since nohup is busy trying to
* disassociate a process from its controlling
* terminal.
*/
stop_time +=
} else {
stop_time = 0;
}
if (do_pnohup(P) == 0)
success = 1;
/*
* If the process was stopped because of
* our call to kill(2) (i.e. if it stopped
* some time after kill_time) then restart
* the process.
*/
Prelease(P, 0);
}
/*
* If we didn't successfully nohup any member of the
* process group.
*/
if (!success)
nerrs++;
}
} else {
for (i = 0; i < argc && !g_interrupt; i++) {
gettext("nohup: cannot examine %s: %s\n"),
nerrs++;
continue;
}
if (do_pnohup(P) != 0)
nerrs++;
Prelease(P, 0);
}
}
return (NOHUP_ERROR);
return (0);
}
#endif /* !XPG4 */