⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 jobs.c

📁 一个开放源代码的 AT&T 的 Korn Shell 的复制品, 支持大多数 ksh89 的特性。
💻 C
📖 第 1 页 / 共 3 页
字号:
/* * Process and job control *//* * Reworked/Rewritten version of Eric Gisin's/Ron Natalie's code by * Larry Bouzane (larry@cs.mun.ca) and hacked again by * Michael Rendell (michael@cs.mun.ca) * * The interface to the rest of the shell should probably be changed * to allow use of vfork() when available but that would be way too much * work :) * * Notes regarding the copious ifdefs: *	- JOB_SIGS is independent of JOBS - it is defined if there are modern *	  signal and wait routines available.  This is prefered, even when *	  JOBS is not defined, since the shell will not otherwise notice when *	  background jobs die until the shell waits for a foreground process *	  to die. *	- TTY_PGRP defined iff JOBS is defined - defined if there are tty *	  process groups *	- NEED_PGRP_SYNC defined iff JOBS is defined - see comment below */#include "sh.h"#include "ksh_stat.h"#include "ksh_wait.h"#include "ksh_times.h"#include "tty.h"/* Start of system configuration stuff *//* We keep CHILD_MAX zombie processes around (exact value isn't critical) */#ifndef CHILD_MAX# if defined(HAVE_SYSCONF) && defined(_SC_CHILD_MAX)#  define CHILD_MAX sysconf(_SC_CHILD_MAX)# else /* _SC_CHILD_MAX */#  ifdef _POSIX_CHILD_MAX#   define CHILD_MAX	((_POSIX_CHILD_MAX) * 2)#  else /* _POSIX_CHILD_MAX */#   define CHILD_MAX	20#  endif /* _POSIX_CHILD_MAX */# endif /* _SC_CHILD_MAX */#endif /* !CHILD_MAX */#ifdef JOBS# if defined(HAVE_TCSETPGRP) || defined(TIOCSPGRP)#  define TTY_PGRP# endif# ifdef BSD_PGRP#  define setpgid	setpgrp#  define getpgID()	getpgrp(0)# else#  define getpgID()	getpgrp()# endif# if defined(TTY_PGRP) && !defined(HAVE_TCSETPGRP)int tcsetpgrp ARGS((int fd, pid_t grp));int tcgetpgrp ARGS((int fd));inttcsetpgrp(fd, grp)	int fd;	pid_t grp;{	return ioctl(fd, TIOCSPGRP, &grp);}inttcgetpgrp(fd)	int	fd;{	int r, grp;	if ((r = ioctl(fd, TIOCGPGRP, &grp)) < 0)		return r;	return grp;}# endif /* !HAVE_TCSETPGRP && TIOCSPGRP */#else /* JOBS *//* These so we can use ifdef xxx instead of if defined(JOBS) && defined(xxx) */# undef TTY_PGRP# undef NEED_PGRP_SYNC#endif /* JOBS *//* End of system configuration stuff *//* Order important! */#define PRUNNING	0#define PEXITED		1#define PSIGNALLED	2#define PSTOPPED	3typedef struct proc	Proc;struct proc {	Proc	*next;		/* next process in pipeline (if any) */	int	state;	WAIT_T	status;		/* wait status */	pid_t	pid;		/* process id */	char	command[48];	/* process command string */};/* Notify/print flag - j_print() argument */#define JP_NONE		0	/* don't print anything */#define JP_SHORT	1	/* print signals processes were killed by */#define JP_MEDIUM	2	/* print [job-num] -/+ command */#define JP_LONG		3	/* print [job-num] -/+ pid command */#define JP_PGRP		4	/* print pgrp *//* put_job() flags */#define PJ_ON_FRONT	0	/* at very front */#define PJ_PAST_STOPPED	1	/* just past any stopped jobs *//* Job.flags values */#define JF_STARTED	0x001	/* set when all processes in job are started */#define JF_WAITING	0x002	/* set if j_waitj() is waiting on job */#define JF_W_ASYNCNOTIFY 0x004	/* set if waiting and async notification ok */#define JF_XXCOM	0x008	/* set for `command` jobs */#define JF_FG		0x010	/* running in foreground (also has tty pgrp) */#define JF_SAVEDTTY	0x020	/* j->ttystate is valid */#define JF_CHANGED	0x040	/* process has changed state */#define JF_KNOWN	0x080	/* $! referenced */#define JF_ZOMBIE	0x100	/* known, unwaited process */#define JF_REMOVE	0x200	/* flaged for removal (j_jobs()/j_noityf()) */#define JF_USETTYMODE	0x400	/* tty mode saved if process exits normally */#define JF_SAVEDTTYPGRP	0x800	/* j->saved_ttypgrp is valid */typedef struct job Job;struct job {	Job	*next;		/* next job in list */	int	job;		/* job number: %n */	int	flags;		/* see JF_* */	int	state;		/* job state */	int	status;		/* exit status of last process */	pid_t	pgrp;		/* process group of job */	pid_t	ppid;		/* pid of process that forked job */	INT32	age;		/* number of jobs started */	clock_t	systime;	/* system time used by job */	clock_t	usrtime;	/* user time used by job */	Proc	*proc_list;	/* process list */	Proc	*last_proc;	/* last process in list */#ifdef KSH	Coproc_id coproc_id;	/* 0 or id of coprocess output pipe */#endif /* KSH */#ifdef TTY_PGRP	TTY_state ttystate;	/* saved tty state for stopped jobs */	pid_t	saved_ttypgrp;	/* saved tty process group for stopped jobs */#endif /* TTY_PGRP */};/* Flags for j_waitj() */#define JW_NONE		0x00#define JW_INTERRUPT	0x01	/* ^C will stop the wait */#define JW_ASYNCNOTIFY	0x02	/* asynchronous notification during wait ok */#define JW_STOPPEDWAIT	0x04	/* wait even if job stopped *//* Error codes for j_lookup() */#define JL_OK		0#define JL_NOSUCH	1	/* no such job */#define JL_AMBIG	2	/* %foo or %?foo is ambiguous */#define JL_INVALID	3	/* non-pid, non-% job id */static const char	*const lookup_msgs[] = {				null,				"no such job",				"ambiguous",				"argument must be %job or process id",				(char *) 0			    };clock_t	j_systime, j_usrtime;	/* user and system time of last j_waitjed job */static Job		*job_list;	/* job list */static Job		*last_job;static Job		*async_job;static pid_t		async_pid;static int		nzombie;	/* # of zombies owned by this process */static INT32		njobs;		/* # of jobs started */static int		child_max;	/* CHILD_MAX */#ifdef JOB_SIGS/* held_sigchld is set if sigchld occurs before a job is completely started */static int		held_sigchld;#endif /* JOB_SIGS */#ifdef JOBSstatic struct shf	*shl_j;#endif /* JOBS */#ifdef NEED_PGRP_SYNC/* On some systems, the kernel doesn't count zombie processes when checking * if a process group is valid, which can cause problems in creating the * pipeline "cmd1 | cmd2": if cmd1 can die (and go into the zombie state) * before cmd2 is started, the kernel doesn't allow the setpgid() for cmd2 * to succeed.  Solution is to create a pipe between the parent and the first * process; the first process doesn't do anything until the pipe is closed * and the parent doesn't close the pipe until all the processes are started. */static int		j_sync_pipe[2];static int		j_sync_open;#endif /* NEED_PGRP_SYNC */#ifdef TTY_PGRPstatic int		ttypgrp_ok;	/* set if can use tty pgrps */static pid_t		restore_ttypgrp = -1;static pid_t		our_pgrp;static int const	tt_sigs[] = { SIGTSTP, SIGTTIN, SIGTTOU };#endif /* TTY_PGRP */static void		j_set_async ARGS((Job *j));static void		j_startjob ARGS((Job *j));static int		j_waitj ARGS((Job *j, int flags, const char *where));static RETSIGTYPE	j_sigchld ARGS((int sig));static void		j_print ARGS((Job *j, int how, struct shf *shf));static Job		*j_lookup ARGS((const char *cp, int *ecodep));static Job		*new_job ARGS((void));static Proc		*new_proc ARGS((void));static void		check_job ARGS((Job *j));static void		put_job ARGS((Job *j, int where));static void		remove_job ARGS((Job *j, const char *where));static void		kill_job ARGS((Job *j));static void	 	fill_command ARGS((char *c, int len, struct op *t));/* initialize job control */voidj_init(mflagset)	int mflagset;{	child_max = CHILD_MAX; /* so syscon() isn't always being called */#ifdef JOB_SIGS	sigemptyset(&sm_default);	sigprocmask(SIG_SETMASK, &sm_default, (sigset_t *) 0);	sigemptyset(&sm_sigchld);	sigaddset(&sm_sigchld, SIGCHLD);	setsig(&sigtraps[SIGCHLD], j_sigchld,		SS_RESTORE_ORIG|SS_FORCE|SS_SHTRAP);#else /* JOB_SIGS */	/* Make sure SIGCHLD isn't ignored - can do odd things under SYSV */	setsig(&sigtraps[SIGCHLD], SIG_DFL, SS_RESTORE_ORIG|SS_FORCE);#endif /* JOB_SIGS */#ifdef JOBS	if (!mflagset && Flag(FTALKING))		Flag(FMONITOR) = 1;	/* shl_j is used to do asynchronous notification (used in	 * an interrupt handler, so need a distinct shf)	 */	shl_j = shf_fdopen(2, SHF_WR, (struct shf *) 0);# ifdef TTY_PGRP	if (Flag(FMONITOR) || Flag(FTALKING)) {		int i;		/* the TF_SHELL_USES test is a kludge that lets us know if		 * if the signals have been changed by the shell.		 */		for (i = NELEM(tt_sigs); --i >= 0; ) {			sigtraps[tt_sigs[i]].flags |= TF_SHELL_USES;			/* j_change() sets this to SS_RESTORE_DFL if FMONITOR */			setsig(&sigtraps[tt_sigs[i]], SIG_IGN,				SS_RESTORE_IGN|SS_FORCE);		}	}# endif /* TTY_PGRP */	/* j_change() calls tty_init() */	if (Flag(FMONITOR))		j_change();	else#endif /* JOBS */	  if (Flag(FTALKING))		tty_init(TRUE);}/* job cleanup before shell exit */voidj_exit(){	/* kill stopped, and possibly running, jobs */	Job	*j;	int	killed = 0;	for (j = job_list; j != (Job *) 0; j = j->next) {		if (j->ppid == procpid		    && (j->state == PSTOPPED			|| (j->state == PRUNNING			    && ((j->flags & JF_FG)				|| (Flag(FLOGIN) && !Flag(FNOHUP)				    && procpid == kshpid)))))		{			killed = 1;			killpg(j->pgrp, SIGHUP);#ifdef JOBS			if (j->state == PSTOPPED)				killpg(j->pgrp, SIGCONT);#endif /* JOBS */		}	}	if (killed)		sleep(1);	j_notify();#ifdef JOBS# ifdef TTY_PGRP	if (kshpid == procpid && restore_ttypgrp >= 0) {		/* Need to restore the tty pgrp to what it was when the		 * shell started up, so that the process that started us		 * will be able to access the tty when we are done.		 * Also need to restore our process group in case we are		 * about to do an exec so that both our parent and the		 * process we are to become will be able to access the tty.		 */		tcsetpgrp(tty_fd, restore_ttypgrp);		setpgid(0, restore_ttypgrp);	}# endif /* TTY_PGRP */	if (Flag(FMONITOR)) {		Flag(FMONITOR) = 0;		j_change();	}#endif /* JOBS */}#ifdef JOBS/* turn job control on or off according to Flag(FMONITOR) */voidj_change(){	int i;	if (Flag(FMONITOR)) {		/* Don't call get_tty() 'til we own the tty process group */		tty_init(FALSE);# ifdef TTY_PGRP		/* no controlling tty, no SIGT* */		ttypgrp_ok = tty_fd >= 0 && tty_devtty;		if (ttypgrp_ok && (our_pgrp = getpgID()) < 0) {			warningf(FALSE, "j_init: getpgrp() failed: %s",				strerror(errno));			ttypgrp_ok = 0;		}		if (ttypgrp_ok) {			setsig(&sigtraps[SIGTTIN], SIG_DFL,				SS_RESTORE_ORIG|SS_FORCE);			/* wait to be given tty (POSIX.1, B.2, job control) */			while (1) {				pid_t ttypgrp;				if ((ttypgrp = tcgetpgrp(tty_fd)) < 0) {					warningf(FALSE,					"j_init: tcgetpgrp() failed: %s",						strerror(errno));					ttypgrp_ok = 0;					break;				}				if (ttypgrp == our_pgrp)					break;				kill(0, SIGTTIN);			}		}		for (i = NELEM(tt_sigs); --i >= 0; )			setsig(&sigtraps[tt_sigs[i]], SIG_IGN,				SS_RESTORE_DFL|SS_FORCE);		if (ttypgrp_ok && our_pgrp != kshpid) {			if (setpgid(0, kshpid) < 0) {				warningf(FALSE,					"j_init: setpgid() failed: %s",					strerror(errno));				ttypgrp_ok = 0;			} else {				if (tcsetpgrp(tty_fd, kshpid) < 0) {					warningf(FALSE,					"j_init: tcsetpgrp() failed: %s",						strerror(errno));					ttypgrp_ok = 0;				} else					restore_ttypgrp = our_pgrp;				our_pgrp = kshpid;			}		}#  if defined(NTTYDISC) && defined(TIOCSETD) && !defined(HAVE_TERMIOS_H) && !defined(HAVE_TERMIO_H)		if (ttypgrp_ok) {			int ldisc = NTTYDISC;			if (ioctl(tty_fd, TIOCSETD, &ldisc) < 0)				warningf(FALSE,				"j_init: can't set new line discipline: %s",					strerror(errno));		}#  endif /* NTTYDISC && TIOCSETD */		if (!ttypgrp_ok)			warningf(FALSE, "warning: won't have full job control");# endif /* TTY_PGRP */		if (tty_fd >= 0)			get_tty(tty_fd, &tty_state);	} else {# ifdef TTY_PGRP		ttypgrp_ok = 0;		if (Flag(FTALKING))			for (i = NELEM(tt_sigs); --i >= 0; )				setsig(&sigtraps[tt_sigs[i]], SIG_IGN,					SS_RESTORE_IGN|SS_FORCE);		else			for (i = NELEM(tt_sigs); --i >= 0; ) {				if (sigtraps[tt_sigs[i]].flags & (TF_ORIG_IGN							          |TF_ORIG_DFL))					setsig(&sigtraps[tt_sigs[i]],						(sigtraps[tt_sigs[i]].flags & TF_ORIG_IGN) ? SIG_IGN : SIG_DFL,						SS_RESTORE_ORIG|SS_FORCE);			}# endif /* TTY_PGRP */		if (!Flag(FTALKING))			tty_close();	}}#endif /* JOBS *//* execute tree in child subprocess */intexchild(t, flags, close_fd)	struct op	*t;	int		flags;	int		close_fd;	/* used if XPCLOSE or XCCLOSE */{	static Proc	*last_proc;	/* for pipelines */	int		i;#ifdef JOB_SIGS	sigset_t	omask;#endif /* JOB_SIGS */	Proc		*p;	Job		*j;	int		rv = 0;	int		forksleep;	int		ischild;	if (flags & XEXEC)		/* Clear XFORK|XPCLOSE|XCCLOSE|XCOPROC|XPIPEO|XPIPEI|XXCOM|XBGND		 * (also done in another execute() below)		 */		return execute(t, flags & (XEXEC | XERROK));#ifdef JOB_SIGS	/* no SIGCHLD's while messing with job and process lists */	sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);#endif /* JOB_SIGS */	p = new_proc();	p->next = (Proc *) 0;	p->state = PRUNNING;	WSTATUS(p->status) = 0;	p->pid = 0;	/* link process into jobs list */	if (flags&XPIPEI) {	/* continuing with a pipe */		if (!last_job)			internal_errorf(1, "exchild: XPIPEI and no last_job - pid %d", (int) procpid);		j = last_job;		last_proc->next = p;		last_proc = p;	} else {#ifdef NEED_PGRP_SYNC		if (j_sync_open) {	/* should never happen */			j_sync_open = 0;			closepipe(j_sync_pipe);		}		/* don't do the sync pipe business if there is no pipeline */		if (flags & XPIPEO) {			openpipe(j_sync_pipe);			j_sync_open = 1;		}#endif /* NEED_PGRP_SYNC */		j = new_job(); /* fills in j->job */		/* we don't consider XXCOM's foreground since they don't get		 * tty process group and we don't save or restore tty modes.		 */		j->flags = (flags & XXCOM) ? JF_XXCOM			: ((flags & XBGND) ? 0 : (JF_FG|JF_USETTYMODE));		j->usrtime = j->systime = 0;		j->state = PRUNNING;		j->pgrp = 0;		j->ppid = procpid;		j->age = ++njobs;		j->proc_list = p;#ifdef KSH		j->coproc_id = 0;#endif /* KSH */		last_job = j;		last_proc = p;		put_job(j, PJ_PAST_STOPPED);	}	fill_command(p->command, sizeof(p->command), t);	/* create child process */	forksleep = 1;	while ((i = fork()) < 0 && errno == EAGAIN && forksleep < 32) {		if (intrsig)	 /* allow user to ^C out... */			break;		sleep(forksleep);		forksleep <<= 1;	}	if (i < 0) {		kill_job(j);		remove_job(j, "fork failed");#ifdef NEED_PGRP_SYNC		if (j_sync_open) {			closepipe(j_sync_pipe);			j_sync_open = 0;		}#endif /* NEED_PGRP_SYNC */#ifdef JOB_SIGS		sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0);#endif /* JOB_SIGS */		errorf("cannot fork - try again");	}	ischild = i == 0;	if (ischild)		p->pid = procpid = getpid();	else		p->pid = i;#ifdef JOBS	/* job control set up */	if (Flag(FMONITOR) && !(flags&XXCOM)) {		int	dotty = 0;# ifdef NEED_PGRP_SYNC		int	first_child_sync = 0;# endif /* NEED_PGRP_SYNC */# ifdef NEED_PGRP_SYNC		if (j_sync_open) {			/*			 * The Parent closes 0, keeps 1 open 'til the whole			 * pipeline is started.  The First child closes 1,			 * keeps 0 open (reads from it).  The remaining			 * children just have to close 1 (parent has already			 * closeed 0).			 */			if (j->pgrp == 0) { /* First process */				close(j_sync_pipe[ischild]);				j_sync_pipe[ischild] = -1;				first_child_sync = ischild;			} else if (ischild) {				j_sync_open = 0;				closepipe(j_sync_pipe);			}		}# endif /* NEED_PGRP_SYNC */		if (j->pgrp == 0) {	/* First process */			j->pgrp = p->pid;			dotty = 1;		}		/* set pgrp in both parent and child to deal with race		 * condition		 */		setpgid(p->pid, j->pgrp);# ifdef TTY_PGRP		/* YYY: should this be		   if (ttypgrp_ok && ischild && !(flags&XBGND))			tcsetpgrp(tty_fd, j->pgrp);		   instead? (see also YYY below)		 */		if (ttypgrp_ok && dotty && !(flags & XBGND))			tcsetpgrp(tty_fd, j->pgrp);# endif /* TTY_PGRP */# ifdef NEED_PGRP_SYNC		if (first_child_sync) {			char c;			while (read(j_sync_pipe[0], &c, 1) == -1			       && errno == EINTR)				;			close(j_sync_pipe[0]);			j_sync_open = 0;		}# endif /* NEED_PGRP_SYNC */	}#endif /* JOBS */	/* used to close pipe input fd */	if (close_fd >= 0 && (((flags & XPCLOSE) && !ischild)			      || ((flags & XCCLOSE) && ischild)))		close(close_fd);	if (ischild) {		/* child */#ifdef KSH		/* Do this before restoring signal */		if (flags & XCOPROC)			coproc_cleanup(FALSE);#endif /* KSH */#ifdef JOB_SIGS		sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0);#endif /* JOB_SIGS */		cleanup_parents_env();#ifdef TTY_PGRP		/* If FMONITOR or FTALKING is set, these signals are ignored,		 * if neither FMONITOR nor FTALKING are set, the signals have		 * their inherited values.		 */		if (Flag(FMONITOR) && !(flags & XXCOM)) {			for (i = NELEM(tt_sigs); --i >= 0; )				setsig(&sigtraps[tt_sigs[i]], SIG_DFL,					SS_RESTORE_DFL|SS_FORCE);		}#endif /* TTY_PGRP */#ifdef HAVE_NICE		if (Flag(FBGNICE) && (flags & XBGND))			nice(4);#endif /* HAVE_NICE */		if ((flags & XBGND) && !Flag(FMONITOR)) {			setsig(&sigtraps[SIGINT], SIG_IGN,				SS_RESTORE_IGN|SS_FORCE);			setsig(&sigtraps[SIGQUIT], SIG_IGN,				SS_RESTORE_IGN|SS_FORCE);			if (!(flags & (XPIPEI | XCOPROC))) {				int fd = open("/dev/null", 0);				(void) ksh_dup2(fd, 0, TRUE);				close(fd);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -