#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 <sys/types.h>
#include <sys/stat.h>
#include <libproc.h>
#include <dirent.h>
#include <ctype.h>
#include <sys/time.h>
#define NOHUP_PERM (S_IRUSR | S_IWUSR)
#define NOHUP_NOEXEC 126
#define NOHUP_ERROR 127
#ifdef XPG4
#define OPTSTR ""
#else
#define OPTSTR "pFag"
static int pnohup(int, char **);
static struct ps_prochandle *g_proc;
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 char nout[PATH_MAX] = "nohup.out";
static int
open_file(void)
{
char *home;
int fd;
int flags = O_CREAT | O_WRONLY | O_APPEND;
if ((fd = open(nout, flags, NOHUP_PERM)) < 0) {
if ((home = getenv("HOME")) == NULL)
return (-1);
if ((snprintf(nout, sizeof (nout),
"%s/nohup.out", home) >= sizeof (nout)) ||
(fd = open(nout, flags, NOHUP_PERM)) < 0) {
return (-1);
}
}
(void) fprintf(stderr, gettext("Sending output to %s\n"), nout);
return (fd);
}
int
main(int argc, char **argv)
{
int fd = -1;
int opt;
int err;
if ((pname = strrchr(argv[0], '/')) == NULL)
pname = argv[0];
else
argv[0] = ++pname;
(void) setlocale(LC_ALL, "");
#ifndef TEXT_DOMAIN
#define TEXT_DOMAIN "SYS_TEST"
#endif
(void) textdomain(TEXT_DOMAIN);
while ((opt = getopt(argc, argv, OPTSTR)) != EOF) {
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;
}
}
argc -= optind;
argv += optind;
if (argc == 0)
goto usage;
#ifndef XPG4
if (opt_p && opt_g)
goto usage;
if (opt_p || opt_g)
return (pnohup(argc, argv));
if (opt_a || opt_F)
goto usage;
#endif
argv[argc] = NULL;
(void) signal(SIGHUP, SIG_IGN);
#ifndef XPG4
(void) signal(SIGQUIT, SIG_IGN);
#endif
if (isatty(STDOUT_FILENO)) {
if ((fd = open_file()) < 0)
goto err;
(void) dup2(fd, STDOUT_FILENO);
}
if (isatty(STDERR_FILENO)) {
if (fd < 0 && (fd = open_file()) < 0)
goto err;
(void) dup2(fd, STDERR_FILENO);
}
if (fd >= 0)
(void) close(fd);
(void) execvp(argv[0], argv);
err = errno;
(void) freopen("/dev/tty", "w", stderr);
(void) fprintf(stderr, gettext("nohup: %s: %s\n"), argv[0],
strerror(err));
return (err == ENOENT ? NOHUP_ERROR : NOHUP_NOEXEC);
err:
(void) fprintf(stderr, gettext("nohup: cannot open/create "
"nohup.out: %s\n"), strerror(errno));
return (NOHUP_ERROR);
usage:
#ifdef XPG4
(void) fprintf(stderr,
gettext("usage: nohup command [argument ...]\n"));
#else
(void) fprintf(stderr, gettext("usage:\n"
"\tnohup command [argument ...]\n"
"\tnohup -p [-Fa] pid [pid ...]\n"
"\tnohup -g [-Fa] pgid [pgid ...]\n"));
#endif
return (NOHUP_ERROR);
}
#ifndef XPG4
typedef int proc_fd_iter_f(void *, int);
static int
Pfd_iter(struct ps_prochandle *P, proc_fd_iter_f *cb, void *data)
{
char file[64];
dirent_t *dentp;
DIR *dirp;
int ret = 0;
if (Pstate(P) == PS_DEAD)
return (-1);
(void) sprintf(file, "/proc/%d/fd", (int)Pstatus(P)->pr_pid);
if ((dirp = opendir(file)) == NULL)
return (-1);
while ((dentp = readdir(dirp)) != NULL) {
if (dentp->d_name[0] == '.')
continue;
if ((ret = cb(data, atoi(dentp->d_name))) != 0)
break;
}
(void) closedir(dirp);
return (ret);
}
static int
fd_cb(void *data, int fd)
{
struct stat64 sbuf;
int flags;
int *fdp;
int oflags;
char *file;
int tmpfd;
if (pr_fstat64(g_proc, fd, &sbuf) == -1 ||
sbuf.st_rdev != Ppsinfo(g_proc)->pr_ttydev)
return (0);
flags = pr_fcntl(g_proc, fd, F_GETFL, NULL, 0);
if ((flags & O_ACCMODE) == O_RDONLY || fd == STDIN_FILENO) {
fdp = &g_rdfd;
oflags = O_RDONLY;
file = "/dev/null";
} else {
fdp = &g_wrfd;
oflags = O_RDWR | O_APPEND;
file = &nout[0];
}
if (*fdp < 0) {
(void) pr_close(g_proc, fd);
tmpfd = pr_open(g_proc, file, oflags, 0);
if (tmpfd < 0) {
(void) fprintf(stderr,
gettext("nohup: process %d cannot open %s: %s\n"),
Pstatus(g_proc)->pr_pid, file, strerror(errno));
goto err;
}
if (tmpfd != fd) {
(void) pr_fcntl(g_proc, tmpfd, F_DUP2FD,
(void *)(uintptr_t)fd, 0);
(void) pr_close(g_proc, tmpfd);
}
*fdp = fd;
} else {
(void) pr_fcntl(g_proc, *fdp, F_DUP2FD, (void *)(uintptr_t)fd,
0);
}
return (0);
err:
tmpfd = pr_open(g_proc, "/dev/tty", O_RDWR, 0);
if (tmpfd != fd && tmpfd >= 0) {
(void) pr_fcntl(g_proc, tmpfd, F_DUP2FD, (void *)(uintptr_t)fd,
0);
(void) pr_close(g_proc, tmpfd);
}
return (1);
}
static int
lwp_restartable(short syscall)
{
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);
}
static int
lwp_abort(void *data, const lwpstatus_t *lsp)
{
struct ps_lwphandle *L;
int err;
if (!(lsp->pr_flags & PR_ASLEEP) || !lwp_restartable(lsp->pr_syscall))
return (0);
L = Lgrab(g_proc, lsp->pr_lwpid, &err);
(void) Lsetrun(L, 0, PRSABORT);
Lfree(L);
g_dirty = 1;
return (0);
}
static int
lwp_restart(void *data, const lwpstatus_t *lsp)
{
struct ps_lwphandle *L;
int err;
if (lsp->pr_flags & PR_ASLEEP) {
if (!lwp_restartable(lsp->pr_syscall))
return (0);
(void) fprintf(stderr, gettext("nohup: LWP %d failed "
"to abort syscall (%d) in process %d\n"),
lsp->pr_lwpid, lsp->pr_syscall, Pstatus(g_proc)->pr_pid);
return (1);
}
if (lsp->pr_why == PR_SYSEXIT && lsp->pr_errno == EINTR) {
L = Lgrab(g_proc, lsp->pr_lwpid, &err);
(void) Lputareg(L, R_R0, ERESTART);
Lsync(L);
Lfree(L);
}
return (0);
}
static int
do_pnohup(struct ps_prochandle *P)
{
int sig = 0;
struct sigaction sa;
const pstatus_t *psp;
psp = Pstatus(P);
(void) Pdstop(P);
if (Pcreate_agent(P) != 0) {
(void) fprintf(stderr, gettext("nohup: cannot control "
"process %d\n"), psp->pr_pid);
goto err_no_agent;
}
if (!opt_a && pr_sigaction(P, SIGHUP, NULL, &sa) != 0) {
(void) fprintf(stderr, gettext("nohup: cannot read "
"disposition of SIGHUP for %d\n"), psp->pr_pid);
goto no_sigs;
}
if (!opt_a && sa.sa_handler != SIG_DFL && sa.sa_handler != SIG_IGN) {
(void) fprintf(stderr, gettext("nohup: SIGHUP already handled "
"by %d; use -a to force process to ignore\n"), psp->pr_pid);
goto no_sigs;
}
if (!opt_a && pr_sigaction(P, SIGQUIT, NULL, &sa) != 0) {
(void) fprintf(stderr, gettext("nohup: cannot read "
"disposition of SIGQUIT for %d\n"), psp->pr_pid);
goto no_sigs;
}
if (!opt_a && sa.sa_handler != SIG_DFL && sa.sa_handler != SIG_IGN) {
(void) fprintf(stderr, gettext("nohup: SIGQUIT already handled "
"by %d; use -a to force process to ignore\n"), psp->pr_pid);
goto no_sigs;
}
sa.sa_handler = SIG_IGN;
if (pr_sigaction(P, SIGHUP, &sa, NULL) != 0) {
(void) fprintf(stderr, gettext("nohup: cannot set "
"disposition of SIGHUP for %d\n"), psp->pr_pid);
goto no_sigs;
}
if (pr_sigaction(P, SIGQUIT, &sa, NULL) != 0) {
(void) fprintf(stderr, gettext("nohup: cannot set "
"disposition of SIGQUIT for %d\n"), psp->pr_pid);
goto no_sigs;
}
no_sigs:
Pdestroy_agent(P);
if ((psp->pr_lwp.pr_flags & PR_STOPPED) &&
psp->pr_lwp.pr_why == PR_JOBCONTROL) {
sig = psp->pr_lwp.pr_what;
(void) kill(psp->pr_pid, SIGCONT);
(void) Pwait(P, 0);
}
(void) Psysexit(P, 0, 1);
g_dirty = 0;
g_proc = P;
(void) Plwp_iter(P, lwp_abort, NULL);
if (g_dirty) {
(void) Pwait(P, 0);
if (Plwp_iter(P, lwp_restart, NULL) != 0)
goto err_no_agent;
}
(void) Psysexit(P, 0, 0);
if (Pcreate_agent(P) != 0) {
(void) fprintf(stderr, gettext("nohup: cannot control "
"process %d\n"), psp->pr_pid);
goto err_no_agent;
}
if (pr_access(P, nout, R_OK | W_OK) != 0) {
(void) fprintf(stderr, gettext("nohup: process %d can not "
"access %s: %s\n"), psp->pr_pid, nout, strerror(errno));
goto err_agent;
}
g_wrfd = -1;
g_rdfd = -1;
(void) Pfd_iter(P, fd_cb, NULL);
Pdestroy_agent(P);
if (sig != 0)
(void) kill(psp->pr_pid, sig);
return (0);
err_agent:
Pdestroy_agent(P);
err_no_agent:
if (sig != 0)
(void) kill(psp->pr_pid, sig);
return (-1);
}
static void
intr(int sig)
{
g_interrupt = 1;
}
static int
pnohup(int argc, char **argv)
{
struct ps_prochandle *P;
int i, j;
int flag = 0;
int gcode;
int nh_fd = -1;
char *fname;
char *home;
int nerrs = 0;
if (sigset(SIGHUP, SIG_IGN) == SIG_DFL)
(void) sigset(SIGHUP, intr);
if (sigset(SIGINT, SIG_IGN) == SIG_DFL)
(void) sigset(SIGINT, intr);
if (sigset(SIGQUIT, SIG_IGN) == SIG_DFL)
(void) sigset(SIGQUIT, intr);
(void) sigset(SIGPIPE, intr);
(void) sigset(SIGTERM, intr);
if (opt_F)
flag |= PGRAB_FORCE;
if (getcwd(nout, sizeof (nout) - strlen("/nohup.out") - 1) != NULL) {
fname = &nout[strlen(nout)];
(void) strcpy(fname, "/nohup.out");
fname++;
nh_fd = open(nout, O_WRONLY | O_CREAT, NOHUP_PERM);
}
if (nh_fd == -1 && (home = getenv("HOME")) != NULL) {
if (snprintf(nout, sizeof (nout),
"%s/nohup.out", home) < sizeof (nout)) {
nh_fd = open(nout, O_WRONLY | O_CREAT, NOHUP_PERM);
fname = &nout[0];
}
}
if (nh_fd == -1) {
(void) fprintf(stderr, gettext("nohup: cannot open/create "
"nohup.out: %s\n"), strerror(errno));
return (NOHUP_ERROR);
}
if (opt_g) {
pid_t *pgids;
int npgids;
int success;
(void) setpgid(0, 0);
pgids = calloc(argc, sizeof (pid_t));
pgids[0] = getpid();
npgids = 1;
for (i = 0; i < argc; i++) {
dirent_t *dent;
DIR *dirp;
psinfo_t psinfo;
const pstatus_t *psp;
pid_t pgid;
char *end;
hrtime_t kill_time, stop_time;
if (isdigit(*argv[i])) {
pgid = strtol(argv[i], &end, 10);
if (*end == '\0' && pgid > 1)
goto pgid_ok;
}
(void) fprintf(stderr, gettext("nohup: "
"bad process group %s\n"), argv[i]);
nerrs++;
continue;
pgid_ok:
for (j = 0; j < npgids; j++) {
if (pgids[j] == pgid)
break;
}
if (j != npgids)
continue;
pgids[npgids++] = pgid;
kill_time = gethrtime();
if (kill(-pgid, SIGSTOP) == -1) {
(void) fprintf(stderr, gettext("nohup: cannot "
"stop process group %d: %s\n"), pgid,
errno != ESRCH ? strerror(errno) :
gettext("No such process group"));
nerrs++;
continue;
}
dirp = opendir("/proc");
success = 0;
while ((dent = readdir(dirp)) != NULL && !g_interrupt) {
if (dent->d_name[0] == '.')
continue;
if (proc_arg_psinfo(dent->d_name,
PR_ARG_PIDS, &psinfo, &gcode) == -1)
continue;
if (psinfo.pr_pgid != pgid)
continue;
if (psinfo.pr_nlwp == 0)
continue;
if ((P = proc_arg_grab(dent->d_name,
PR_ARG_PIDS, flag, &gcode)) == NULL) {
(void) fprintf(stderr, gettext("nohup: "
"cannot examine %s: %s\n"),
dent->d_name, Pgrab_error(gcode));
(void) kill(psinfo.pr_pid, SIGCONT);
continue;
}
psp = Pstatus(P);
if (psp->pr_lwp.pr_why == PR_JOBCONTROL) {
stop_time =
psp->pr_lwp.pr_tstamp.tv_sec;
stop_time *= (hrtime_t)NANOSEC;
stop_time +=
psp->pr_lwp.pr_tstamp.tv_nsec;
} else {
stop_time = 0;
}
if (do_pnohup(P) == 0)
success = 1;
if (kill_time <= stop_time)
(void) kill(psinfo.pr_pid, SIGCONT);
Prelease(P, 0);
}
if (!success)
nerrs++;
(void) closedir(dirp);
}
} else {
for (i = 0; i < argc && !g_interrupt; i++) {
if ((P = proc_arg_grab(argv[i], PR_ARG_PIDS, flag,
&gcode)) == NULL) {
(void) fprintf(stderr,
gettext("nohup: cannot examine %s: %s\n"),
argv[i], Pgrab_error(gcode));
nerrs++;
continue;
}
if (do_pnohup(P) != 0)
nerrs++;
Prelease(P, 0);
}
}
(void) close(nh_fd);
if (argc == nerrs)
return (NOHUP_ERROR);
(void) fprintf(stderr, gettext("Sending output to %s\n"), fname);
return (0);
}
#endif