/*
* Don't search the path if a pathname is given
* (if a '/' appears in argv[1]);
* exit if argc==1;
* Don't trash the PATH variable (put back colons!)
*
* This is a grungy little program for executing programs in the
* background, without use of a control terminal. (In the style
* of most common daemon processes...) The intent was to create a
* program one could start via rsh, to initiate xterm sessions,
* without keeping extra local rsh & remote rshd and shell processes
* alive.
*
* Could be a bit less cavalier about return codes...
*
* No warrantees. Use at your own risk. The authors are not responsible
* in any way for anything at all.
*
*/
#ifndef lint
static char rcsid[] = "$Id: detach.c,v 1.5 1991/10/22 13:55:12 anund Exp $";
#endif
/*
* Times before retrying fork if fork fails temporarily (in seconds)
*/
#define RETRY_INITIAL 1
#define RETRY_MAX 4
#ifdef WAITSTART
/*
* Constants controlling wait for subprocess startup
*/
#define LOAD_LIMIT 1.0 /* Load limit, below this no wait */
#define SEC_QUIET 4 /* Seconds with quiet child before quitting */
#define Q_FAULTS 1 /* Number of pagefaults allowed when quiet */
#endif
#ifdef SYS_V
#include <fcntl.h>
#endif /* SYS_V */
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <sys/fcntl.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/wait.h>
#ifdef SYS_V
#define index strchr
#endif /* SYS_V */
#ifdef WAITSTART
#include <kvm.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/time.h>
#include <sys/proc.h>
#include <sys/user.h>
#include <sys/resource.h>
#if defined(sequent)
# include <i386/vmparam.h>
#endif
#include <nlist.h>
#endif
extern int errno;
#include <sys/errno.h>
/*
* Fork in a safe fashion: Check error codes and retry if temporary failure
*/
int safe_fork()
{
int cc, retry = RETRY_INITIAL;
while ((cc = fork()) == -1 && errno == EAGAIN)
{
sleep(retry);
if (retry < RETRY_MAX) retry *= 2; /* Exponential backoff */
}
if (cc == -1) /* Fatal error */
{
perror("Can't fork");
exit(-1);
}
return cc;
}
#ifdef WAITSTART
/*
* Get load average
*/
#ifdef HAS_KVM
#define KVM_T kvm_t *
#else
#define KVM_T int
#endif
struct nlist nl[] = {
{ "_avenrun" },
{ 0 },
};
static KVM_T kmem_init()
{
KVM_T kmem;
#ifdef HAS_KVM
kmem = kvm_open((char *)NULL, (char *) NULL, (char *) NULL,
O_RDONLY, "detach");
if (!kmem)
exit(-1);
#endif
#ifdef sgi
# include <sys/sysmp.h>
nl_avenrun[0].n_value = sysmp(MP_KERNADDR, MPKA_AVENRUN) & 0x7fffffff;
#else
# ifdef HAS_KVM
kvm_nlist(kmem, nl);
# else
nlist("/vmunix", nl);
# endif
if (nl[0].n_type==0) {
fputs("detach: /vmunix has no namelist\n",stderr);
exit(-1);
}
#endif
#ifndef HAS_KVM
if((kmem = open("/dev/kmem", 0)) == -1) {
perror("detach: can't open(/dev/kmem)");
exit(-1);
}
#endif
return kmem;
}
#ifndef HAS_KVM
int kvm_read(kd, addr, buf, nbytes)
int kd;
unsigned long addr;
char *buf;
unsigned nbytes;
{
if( lseek(kd, addr, 0) == -1 )
{
return -1;
}
return read(kmem, addr, nbytes);
}
#endif
static getload(a, kmem)
float a[];
KVM_T kmem;
{
int i;
#ifdef vax
double avenrun[3];
#else
long avenrun[3];
#endif
if (kvm_read(kmem, nl[0].n_value, (char *) avenrun, sizeof(avenrun))
!= sizeof(avenrun))
{
perror("detach: can't read kmem");
exit(-1);
}
for (i = 0; i < 3; i++)
#if defined(sun) || defined(sequent)
a[i] = (float) avenrun[i] / FSCALE;
#else
#ifdef sgi
a[i] = (float) avenrun[i] / 1024;
#else
#ifdef BSD4_2
a[i] = (float) avenrun[i];
#else
a[i] = (float) avenrun[i] / 1024;
#endif /*BSD4_2*/
#endif /*sgi*/
#endif /*sun*/
return;
}
/*
* Wait for process pid to finish initialization and sleep.
*/
waitstart(pid)
int pid;
{
float loadavg[3];
struct proc *pd;
struct user *u;
long prev_faults = 0;
int count = 0;
KVM_T kmem;
/*
* Open kmem and get namelist
*/
kmem = kmem_init();
/*
* First, test load average. Priority one is fast turnaround
* if machine is lightly loaded.
*/
getload(loadavg, kmem);
if (loadavg[0] < (float) LOAD_LIMIT) return;
/*
* Then, hang around until child process is quiescent.
*/
for(;;)
{
pd = kvm_getproc(kmem, pid);
if (!pd)
{
fputs("detach: couldn't read proc entry of child\n", stderr);
exit(-1);
}
if (pd->p_stat == SZOMB)
break; /* No point in going on, child died */
u = kvm_getu(kmem, pd);
if (!u)
{
fputs("detach: couldn't read u structure of child\n", stderr);
exit(-1);
}
#ifdef DEBUG
printf("cpu = %d, slptime = %d, cpticks = %d, pctcpu = %ld \n",
pd->p_cpu,pd->p_slptime,pd->p_cpticks,pd->p_pctcpu);
printf("minflt = %ld, majflt = %ld, nswap = %ld, nvcsw = %ld, nivcsw = %ld \n",
u->u_ru.ru_minflt, u->u_ru.ru_majflt, u->u_ru.ru_nswap, u->u_ru.ru_nvcsw, u->u_ru.ru_nivcsw);
#endif
if ((u->u_ru.ru_majflt - prev_faults) <= Q_FAULTS)
{
count++;
if (count > SEC_QUIET) break;
}
else
count = 0;
prev_faults = u->u_ru.ru_majflt;
sleep(1);
}
kvm_close(kmem);
}
#endif
/*
* Create daemon subprocess
*/
detach(cmd, args)
char *cmd;
char **args;
{
int fd, pid, status;
#ifdef SIGTTOU
signal(SIGTTOU, SIG_IGN);
signal(SIGTTIN, SIG_IGN);
signal(SIGTSTP, SIG_IGN);
#endif /* SIGTTOU */
if (pid = safe_fork())
{ /* parent */
#ifdef WAITSTART
waitstart(pid); /* wait for child to get going */
#else
sleep(1);
#endif
#ifdef SYS_V
exit(0);
#else
if (waitpid(pid, &status, WNOHANG))
{
if (WIFEXITED(status))
{
fprintf(stderr,"detach: child process exited with status %d\n",
WEXITSTATUS(status));
exit(WEXITSTATUS(status));
}
else if (WIFSIGNALED(status))
{
fprintf(stderr,"detach: child process killed by signal %d\n",
WTERMSIG(status));
exit(-1);
}
else
exit(0);
}
else
exit(0);
#endif
}
#ifdef WAITSTART
setegid(getgid()); /* May be running setgid kvm */
#endif
/*
* Three alternate ways of losing controlling terminal is here.
* The new, posix way is the default.
* Define OLDBSD or OLDSYSV if you don't have setsid.
*/
#if defined(OLDSYSV)
/* child */
setpgrp(); /* lose controlling terminal & */
/* change process group */
signal(SIGHUP, SIG_IGN); /* immune from pgrp leader death */
if (safe_fork()) /* become non-pgrp-leader */
exit(0);
/* second child */
#elif defined(OLDBSD)
setpgrp(0, getpid()); /* change process group */
#else
if (setsid() == -1) {
perror("Can't setsid");
exit(1);
}
#endif
#ifdef TIOCNOTTY
if ((fd = open("/dev/tty", O_RDWR)) >= 0) {
ioctl(fd, TIOCNOTTY, 0); /* lose controlling terminal */
close(fd);
}
#endif
/*
* Close all open file descriptors.
* We may want to keep those which goes to a file open, so you
* can do "detach make >& file" - later.
* This is non-portable. Blame IBM, whose getdtablesize return 2 Giga.
*/
for (fd=0; fd < 256; fd++)
close(fd);
/*
* ensure that we have stdin/stdout/stderr open to *somewhere*
*/
open("/dev/null", O_RDWR);
dup2(0, 1);
dup2(0, 2);
/* umask(0); */ /* don't change this! inherit instead. -he */
/* Set signals back to default */
signal (SIGHUP, SIG_DFL);
#ifdef SIGTTOU
signal(SIGTTOU, SIG_DFL);
signal(SIGTTIN, SIG_DFL);
signal(SIGTSTP, SIG_DFL);
#endif /* SIGTTOU */
/* spawn off the wanted command */
execv(cmd, args);
/* If that didn't work, return an error */
perror(cmd);
exit(1);
}
main(argc, argv)
int argc;
char *argv[];
{
char *path, *getenv(), *index(), *i, *fixcolon, j[1024];
if (argc == 1)
exit(0);
/* if the cmd to run in daemon mode has a '/' in it, the path
* has been specified, so don't search the path for it;
*/
if (index(argv[1], '/')) {
detach(argv[1],&argv[1]);
}
else {
path = getenv("PATH");
while (path) {
fixcolon=0;
i = index(path, ':');
if (i) {
*i = 0;
fixcolon=i;
i++;
}
strcpy(j, path);
strcat(j, "/");
strcat(j, argv[1]);
if (fixcolon)
*fixcolon = ':';
if (!access(j, X_OK || F_OK)) {
detach(j, &argv[1]);
}
path = i;
}
}
}
syntax highlighted by Code2HTML, v. 0.9.1