[comp.unix.wizards] utmp entry -- we're not doing something right -- can you help?

irv@happym.wa.com (Irving Wolfe) (07/07/90)

The following program is intended to create a process and re-create it if it 
dies, as if it had been listed in inittab with respawn set, but later, so that 
(for example) a privileged process could start up something which would be 
re-created as often as necessary during a two hour observation period, then 
after 2 hours kill the "gspawn" process and finally kill the most recent 
instance of the process gspawn had created. 

It seems to work, but it doesn't work perfectly in that "ps -f" no longer can 
find the command string associated with many of the processes that are running 
on the system and resorts to showing things like "[ ps ]" instead of "ps 
-fuirv" in the command field. 

Can anyone explain why, preferably (but not necessarily) with a fix?

Thanks!


/* gspawn [ -c prog ] tty [ args... ] */
/* vi:se ts=4 wm=0 sw=4: */

#include <stdio.h>
#include <errno.h>
#include <pwd.h>
#include <string.h>
#include <sys/types.h>
#include <utmp.h>
#include <signal.h>

#define MAXARGS		5
#define ERRTTY		"/dev/console"
#define DEFAULTARG0	"/bin/true"
#define DEFAULTARG2	"9600"

char *Argv[MAXARGS] = {
DEFAULTARG0,
"/dev/null",
DEFAULTARG2,
(char *)0,
};

char *Gettyprog = DEFAULTARG0;
char *Tty;
int Pid = -1;
char *Progname = "gspawn";


/* not entirely portable, but probably more so than varargs :-( */
conerr(msg,a1,a2,a3,a4,a5,a6)
char *msg;
int a1,a2,a3,a4,a5,a6;
{
	char buf[BUFSIZ];
	FILE *f = fopen(ERRTTY, "w");
	if (!f) pause(); /* foo */
	setbuf(f, NULL);
	strcat(buf, Progname);
	sprintf(buf+strlen(buf), msg, a1,a2,a3,a4,a5,a6);
	strcat(buf,"\r\n");
	fputs(buf, f);
	fclose(f);
}

usage() {
fprintf(stderr, "usage: %s [ -c cmd ] tty [ args ... ]\n", Progname);
}

char *
basename(s)
char *s;
{
	char *p = strrchr(s, '/');
	if (p) return ++p;
	return s;
}

cleanup()
{
	/* the heck with it.  */
	exit(0);
}


main(argc, argv)
char **argv;
{
	int c;
	int forks;

	extern char *optarg;
	extern int optind;
	extern int errno;

	Progname = basename(argv[0]);

	while ((c = getopt(argc, argv, "c:")) != EOF)
	switch(c) {
		case 'c':	Argv[0] = optarg; break;
		default:	usage(); exit(1);
	}

	if (optind >= argc) {
		usage();
		exit(2);
	}


	c = 1;
	while (optind < argc)
		Argv[c++] = argv[optind++];
	Argv[c] = 0;
	Tty = Argv[1];

/* if not started by init, daemonize us */
	if (getppid() != 1) {
		Pid = fork();
		if (Pid < 0) {
			perror("fork");
			exit(3);
		} else if (Pid > 0) {
			exit(0);
		} else {
			close(0);
			close(1);
			close(2);
		}
	}
	setpgrp();


	signal(SIGTERM, cleanup);

	forks = 0;

	/* only go away if we're all hosed */
	while (forks < 10) {

		/* fork, make new pgrp, exec cmd */
		Pid = fork();

		if (Pid == 0) {
			/* Make an INIT_PROCESS in utmp */
			do_utmp(Tty);
			for (c = 0; c < _NFILE; c++) close(c);
			setpgrp();
			execv(Argv[0], Argv);
			conerr("Failed miserably");
			exit(1); /* STOP IMMEDIATELY */ 
		} else if (Pid < 0) {
			conerr("couldn't fork in %s\n", argv[0]);
			++forks;
			sleep(200);	/* alarm(20); pause(); */
			continue;
		}

		forks = 0;
		while ((c=wait((int *)0)) != Pid && (c!= -1 || errno==EINTR))
		 ;
		Pid = -1;
	}
}

do_utmp(tty)
char *tty;
{
/* diddling /etc/utmp...   When a cmd process is created, make utmp */
/* entry for new process.  Leave the old one alone so init can find it. */

	extern struct utmp *getutent(), *pututline();
	register struct utmp *u;
	struct utmp ut;
	register FILE *fp;
	char id[4];
	int oldpid = getppid();
	int newpid = getpid();

/* Look in "utmp" for our parent's entry.  It will be an INIT_PROCESS.  */
/* Leave it alone and make a similar entry for us (the child).  There */
/* may already be an entry, but it has the wrong pid. */

/* MAGIC RULE:  id tags should be e.g. p1, p2, p3 and new ones will */
/* be created: P1, P2, P3. */

/* find parent's line, just remember id */
	while ((u = getutent()) != NULL) {
		if (u->ut_type == INIT_PROCESS && u->ut_pid == oldpid) {
			strncpy(id, u->ut_id, sizeof(u->ut_id));
			break;
		}
	}

/* look for child's line */
	if (u != NULL) {
		id[0] = 'P';	/* cheat */
	} else {
		/* testing - not run by init */
		strcpy(id, "PX");
	}

	setutent();

	while ((u = getutent()) != NULL) {
		if (strncmp(u->ut_id, id, sizeof(u->ut_id)) == 0)
			break;
	}

/* regardless of whether we found one, fix it up and write it. */
	if (u == NULL) u = &ut;

	strncpy(u->ut_user, "GETTY", sizeof(u->ut_user));
	strncpy(u->ut_id, id, sizeof(u->ut_user));
	strncpy(u->ut_line, Tty, sizeof(u->ut_line));
	u->ut_pid = newpid;
	u->ut_type = INIT_PROCESS;
	time(&u->ut_time);
	pututline(u);

/* If we were successful in finding an entry for ourself in the */
/* utmp file, then attempt to append to the end of the wtmp file. */
	if (u != NULL && (fp = fopen(WTMP_FILE,"r+")) != NULL) {
		fseek(fp,0L,2);	/* Seek to end of file */
		fwrite(u,sizeof(*u),1,fp);
		fclose(fp);
	}

/* Close the utmp file. */
	endutent();
}

-- 
 Irving Wolfe    Happy Man Corp.   irv@happym.wa.com    206/463-9399 ext.101
 SOLID VALUE, the investment letter for Benj. Graham's intelligent investors
 Information (not sample) free: email patty@happym.wa.com with US mail addr.

cpcahil@virtech.uucp (Conor P. Cahill) (07/07/90)

In article <327@happym.wa.com> irv@happym.wa.com (Irving Wolfe) writes:
>It seems to work, but it doesn't work perfectly in that "ps -f" no longer can 
>find the command string associated with many of the processes that are running 
>on the system and resorts to showing things like "[ ps ]" instead of "ps 
>-fuirv" in the command field. 

having the command appear as [command] in ps -f output normally indicates
that the process is swapped out.  Your program can't be doing to do anything
that will cause this to happen (other than eating up lots of memory and 
then pausing so that it is more likely to get swapped)

>Can anyone explain why, preferably (but not necessarily) with a fix?

Since the problem is that processes are getting swapped, you need to 
do one of the following:

	1. nothing (there is nothing wrong, other than performance, when
	   some processes get swapped).
	2. add more memory
	3. do less processing
	4. go throught your programs and shrink the memory requirements
	5. or some other thing to make more memory available.

-- 
Conor P. Cahill            (703)430-9247        Virtual Technologies, Inc.,
uunet!virtech!cpcahil                           46030 Manekin Plaza, Suite 160
                                                Sterling, VA 22170