"README.md" did not exist on "e8e67bd4a4cabaae98dcbfac79c1d4b84066bf1c"
exit.c 11.2 KB
Newer Older
1 2 3 4 5 6
/*
 *  linux/kernel/exit.c
 *
 *  (C) 1991  Linus Torvalds
 */

Linus Torvalds's avatar
Linus Torvalds committed
7 8
#define DEBUG_PROC_TREE

9 10 11 12 13 14 15 16 17 18 19
#include <errno.h>
#include <signal.h>
#include <sys/wait.h>

#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/tty.h>
#include <asm/segment.h>

int sys_close(int fd);

20
int send_sig(long sig,struct task_struct * p,int priv)
21 22 23
{
	if (!p || (sig < 0) || (sig > 32))
		return -EINVAL;
24 25
	if (!priv && ((sig != SIGCONT) || (current->session != p->session)) &&
	    (current->euid != p->euid) && !suser())
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
		return -EPERM;
	if (!sig)
		return 0;
	if ((sig == SIGKILL) || (sig == SIGCONT)) {
		if (p->state == TASK_STOPPED)
			p->state = TASK_RUNNING;
		p->exit_code = 0;
		p->signal &= ~( (1<<(SIGSTOP-1)) | (1<<(SIGTSTP-1)) |
				(1<<(SIGTTIN-1)) | (1<<(SIGTTOU-1)) );
	} 
	/* Depends on order SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU */
	if ((sig >= SIGSTOP) && (sig <= SIGTTOU)) 
		p->signal &= ~(1<<(SIGCONT-1));
	/* Actually deliver the signal */
	p->signal |= (1<<(sig-1));
	if (p->flags & PF_PTRACED) {
		/* save the signal number for wait. */
		p->exit_code = sig;

		/* we have to make sure the parent is awake. */
		if (p->p_pptr != NULL && p->p_pptr->state == TASK_INTERRUPTIBLE)
			p->p_pptr->state = TASK_RUNNING;

		/* we have to make sure that the process stops. */
		if (p->state == TASK_INTERRUPTIBLE || p->state == TASK_RUNNING)
			p->state = TASK_STOPPED;
	}
	return 0;
}

56 57 58 59 60 61
void release(struct task_struct * p)
{
	int i;

	if (!p)
		return;
Linus Torvalds's avatar
Linus Torvalds committed
62 63 64 65
	if (p == current) {
		printk("task releasing itself\n\r");
		return;
	}
66
	for (i=1 ; i<NR_TASKS ; i++)
67 68
		if (task[i] == p) {
			task[i] = NULL;
Linus Torvalds's avatar
Linus Torvalds committed
69 70 71 72 73 74 75
			/* Update links */
			if (p->p_osptr)
				p->p_osptr->p_ysptr = p->p_ysptr;
			if (p->p_ysptr)
				p->p_ysptr->p_osptr = p->p_osptr;
			else
				p->p_pptr->p_cptr = p->p_osptr;
76
			free_page((long) p);
77 78 79 80 81
			return;
		}
	panic("trying to release non-existent task");
}

Linus Torvalds's avatar
Linus Torvalds committed
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170
#ifdef DEBUG_PROC_TREE
/*
 * Check to see if a task_struct pointer is present in the task[] array
 * Return 0 if found, and 1 if not found.
 */
int bad_task_ptr(struct task_struct *p)
{
	int 	i;

	if (!p)
		return 0;
	for (i=0 ; i<NR_TASKS ; i++)
		if (task[i] == p)
			return 0;
	return 1;
}
	
/*
 * This routine scans the pid tree and make sure the rep invarient still
 * holds.  Used for debugging only, since it's very slow....
 *
 * It looks a lot scarier than it really is.... we're doing nothing more
 * than verifying the doubly-linked list foundin p_ysptr and p_osptr, 
 * and checking it corresponds with the process tree defined by p_cptr and 
 * p_pptr;
 */
void audit_ptree()
{
	int	i;

	for (i=1 ; i<NR_TASKS ; i++) {
		if (!task[i])
			continue;
		if (bad_task_ptr(task[i]->p_pptr))
			printk("Warning, pid %d's parent link is bad\n",
				task[i]->pid);
		if (bad_task_ptr(task[i]->p_cptr))
			printk("Warning, pid %d's child link is bad\n",
				task[i]->pid);
		if (bad_task_ptr(task[i]->p_ysptr))
			printk("Warning, pid %d's ys link is bad\n",
				task[i]->pid);
		if (bad_task_ptr(task[i]->p_osptr))
			printk("Warning, pid %d's os link is bad\n",
				task[i]->pid);
		if (task[i]->p_pptr == task[i])
			printk("Warning, pid %d parent link points to self\n");
		if (task[i]->p_cptr == task[i])
			printk("Warning, pid %d child link points to self\n");
		if (task[i]->p_ysptr == task[i])
			printk("Warning, pid %d ys link points to self\n");
		if (task[i]->p_osptr == task[i])
			printk("Warning, pid %d os link points to self\n");
		if (task[i]->p_osptr) {
			if (task[i]->p_pptr != task[i]->p_osptr->p_pptr)
				printk(
			"Warning, pid %d older sibling %d parent is %d\n",
				task[i]->pid, task[i]->p_osptr->pid,
				task[i]->p_osptr->p_pptr->pid);
			if (task[i]->p_osptr->p_ysptr != task[i])
				printk(
		"Warning, pid %d older sibling %d has mismatched ys link\n",
				task[i]->pid, task[i]->p_osptr->pid);
		}
		if (task[i]->p_ysptr) {
			if (task[i]->p_pptr != task[i]->p_ysptr->p_pptr)
				printk(
			"Warning, pid %d younger sibling %d parent is %d\n",
				task[i]->pid, task[i]->p_osptr->pid,
				task[i]->p_osptr->p_pptr->pid);
			if (task[i]->p_ysptr->p_osptr != task[i])
				printk(
		"Warning, pid %d younger sibling %d has mismatched os link\n",
				task[i]->pid, task[i]->p_ysptr->pid);
		}
		if (task[i]->p_cptr) {
			if (task[i]->p_cptr->p_pptr != task[i])
				printk(
			"Warning, pid %d youngest child %d has mismatched parent link\n",
				task[i]->pid, task[i]->p_cptr->pid);
			if (task[i]->p_cptr->p_ysptr)
				printk(
			"Warning, pid %d youngest child %d has non-NULL ys link\n",
				task[i]->pid, task[i]->p_cptr->pid);
		}
	}
}
#endif /* DEBUG_PROC_TREE */

171 172 173 174 175
/*
 * This checks not only the pgrp, but falls back on the pid if no
 * satisfactory prgp is found. I dunno - gdb doesn't work correctly
 * without this...
 */
Linus Torvalds's avatar
Linus Torvalds committed
176
int session_of_pgrp(int pgrp)
177
{
Linus Torvalds's avatar
Linus Torvalds committed
178
	struct task_struct **p;
179
	int fallback;
Linus Torvalds's avatar
Linus Torvalds committed
180

181 182 183 184
	fallback = -1;
 	for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) {
 		if (!*p || (*p)->session <= 0)
 			continue;
Linus Torvalds's avatar
Linus Torvalds committed
185
		if ((*p)->pgrp == pgrp)
186 187 188 189 190
			return (*p)->session;
		if ((*p)->pid == pgrp)
			fallback = (*p)->session;
	}
	return fallback;
Linus Torvalds's avatar
Linus Torvalds committed
191 192 193 194 195 196 197 198
}

int kill_pg(int pgrp, int sig, int priv)
{
	struct task_struct **p;
	int err,retval = -ESRCH;
	int found = 0;

199
	if (sig<0 || sig>32 || pgrp<=0)
Linus Torvalds's avatar
Linus Torvalds committed
200 201
		return -EINVAL;
 	for (p = &LAST_TASK ; p > &FIRST_TASK ; --p)
202
		if (*p && (*p)->pgrp == pgrp) {
Linus Torvalds's avatar
Linus Torvalds committed
203 204 205 206 207 208 209 210 211 212 213 214
			if (sig && (err = send_sig(sig,*p,priv)))
				retval = err;
			else
				found++;
		}
	return(found ? 0 : retval);
}

int kill_proc(int pid, int sig, int priv)
{
 	struct task_struct **p;

215
	if (sig<0 || sig>32)
Linus Torvalds's avatar
Linus Torvalds committed
216 217
		return -EINVAL;
	for (p = &LAST_TASK ; p > &FIRST_TASK ; --p)
218
		if (*p && (*p)->pid == pid)
Linus Torvalds's avatar
Linus Torvalds committed
219 220
			return(sig ? send_sig(sig,*p,priv) : 0);
	return(-ESRCH);
221 222 223
}

/*
Linus Torvalds's avatar
Linus Torvalds committed
224 225
 * POSIX specifies that kill(-1,sig) is unspecified, but what we have
 * is probably wrong.  Should make it like BSD or SYSV.
226 227
 */
int sys_kill(int pid,int sig)
228 229
{
	struct task_struct **p = NR_TASKS + task;
230
	int err, retval = 0, count = 0;
231

Linus Torvalds's avatar
Linus Torvalds committed
232
	if (!pid)
233
		return(kill_pg(current->pgrp,sig,0));
Linus Torvalds's avatar
Linus Torvalds committed
234 235
	if (pid == -1) {
		while (--p > &FIRST_TASK)
236
			if (*p && (*p)->pid > 1 && *p != current) {
237 238 239 240 241
				++count;
				if ((err = send_sig(sig,*p,0)) != -EPERM)
					retval = err;
			}
		return(count ? retval : -ESRCH);
Linus Torvalds's avatar
Linus Torvalds committed
242 243 244 245 246
	}
	if (pid < 0) 
		return(kill_pg(-pid,sig,0));
	/* Normal kill */
	return(kill_proc(pid,sig,0));
247 248
}

Linus Torvalds's avatar
Linus Torvalds committed
249 250 251 252 253 254 255 256 257
/*
 * Determine if a process group is "orphaned", according to the POSIX
 * definition in 2.2.2.52.  Orphaned process groups are not to be affected
 * by terminal-generated stop signals.  Newly orphaned process groups are 
 * to receive a SIGHUP and a SIGCONT.
 * 
 * "I ask you, have you ever known what it is to be an orphan?"
 */
int is_orphaned_pgrp(int pgrp)
258
{
Linus Torvalds's avatar
Linus Torvalds committed
259
	struct task_struct **p;
260

Linus Torvalds's avatar
Linus Torvalds committed
261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278
	for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) {
		if (!(*p) ||
		    ((*p)->pgrp != pgrp) || 
		    ((*p)->state == TASK_ZOMBIE) ||
		    ((*p)->p_pptr->pid == 1))
			continue;
		if (((*p)->p_pptr->pgrp != pgrp) &&
		    ((*p)->p_pptr->session == (*p)->session))
			return 0;
	}
	return(1);	/* (sighing) "Often!" */
}

static int has_stopped_jobs(int pgrp)
{
	struct task_struct ** p;

	for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) {
279
		if (!*p || (*p)->pgrp != pgrp)
Linus Torvalds's avatar
Linus Torvalds committed
280 281 282 283 284
			continue;
		if ((*p)->state == TASK_STOPPED)
			return(1);
	}
	return(0);
285 286
}

Linus Torvalds's avatar
Linus Torvalds committed
287
volatile void do_exit(long code)
288
{
Linus Torvalds's avatar
Linus Torvalds committed
289
	struct task_struct *p;
290 291 292 293 294 295 296 297
	int i;

	free_page_tables(get_base(current->ldt[1]),get_limit(0x0f));
	free_page_tables(get_base(current->ldt[2]),get_limit(0x17));
	for (i=0 ; i<NR_OPEN ; i++)
		if (current->filp[i])
			sys_close(i);
	iput(current->pwd);
Linus Torvalds's avatar
Linus Torvalds committed
298
	current->pwd = NULL;
299
	iput(current->root);
Linus Torvalds's avatar
Linus Torvalds committed
300
	current->root = NULL;
Linus Torvalds's avatar
Linus Torvalds committed
301
	iput(current->executable);
Linus Torvalds's avatar
Linus Torvalds committed
302
	current->executable = NULL;
303 304 305 306
	for (i=0; i < current->numlibraries; i++) {
		iput(current->libraries[i].library);
		current->libraries[i].library = NULL;
	}	
307 308
	current->state = TASK_ZOMBIE;
	current->exit_code = code;
309
	current->rss = 0;
Linus Torvalds's avatar
Linus Torvalds committed
310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326
	/* 
	 * Check to see if any process groups have become orphaned
	 * as a result of our exiting, and if they have any stopped
	 * jobs, send them a SIGUP and then a SIGCONT.  (POSIX 3.2.2.2)
	 *
	 * Case i: Our father is in a different pgrp than we are
	 * and we were the only connection outside, so our pgrp
	 * is about to become orphaned.
 	 */
	if ((current->p_pptr->pgrp != current->pgrp) &&
	    (current->p_pptr->session == current->session) &&
	    is_orphaned_pgrp(current->pgrp) &&
	    has_stopped_jobs(current->pgrp)) {
		kill_pg(current->pgrp,SIGHUP,1);
		kill_pg(current->pgrp,SIGCONT,1);
	}
	/* Let father know we died */
327
	send_sig (SIGCHLD, current->p_pptr, 1);
Linus Torvalds's avatar
Linus Torvalds committed
328 329 330 331 332 333 334 335 336
	
	/*
	 * This loop does two things:
	 * 
  	 * A.  Make init inherit all the child processes
	 * B.  Check to see if any process groups have become orphaned
	 *	as a result of our exiting, and if they have any stopped
	 *	jons, send them a SIGUP and then a SIGCONT.  (POSIX 3.2.2.2)
	 */
337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358
	while (p = current->p_cptr) {
		current->p_cptr = p->p_osptr;
		p->p_ysptr = NULL;
	        p->flags &= ~PF_PTRACED;
		p->p_pptr = task[1];
		p->p_osptr = task[1]->p_cptr;
		task[1]->p_cptr->p_ysptr = p;
		task[1]->p_cptr = p;
		if (p->state == TASK_ZOMBIE)
			task[1]->signal |= (1<<(SIGCHLD-1));
		/*
		 * process group orphan check
		 * Case ii: Our child is in a different pgrp 
		 * than we are, and it was the only connection
		 * outside, so the child pgrp is now orphaned.
		 */
		if ((p->pgrp != current->pgrp) &&
		    (p->session == current->session) &&
		    is_orphaned_pgrp(p->pgrp) &&
		    has_stopped_jobs(p->pgrp)) {
			kill_pg(p->pgrp,SIGHUP,1);
			kill_pg(p->pgrp,SIGCONT,1);
Linus Torvalds's avatar
Linus Torvalds committed
359 360 361 362 363 364 365 366
		}
	}
	if (current->leader) {
		struct task_struct **p;
		struct tty_struct *tty;

		if (current->tty >= 0) {
			tty = TTY_TABLE(current->tty);
367
			if (tty->pgrp > 0)
Linus Torvalds's avatar
Linus Torvalds committed
368
				kill_pg(tty->pgrp, SIGHUP, 1);
369
			tty->pgrp = -1;
Linus Torvalds's avatar
Linus Torvalds committed
370 371 372
			tty->session = 0;
		}
	 	for (p = &LAST_TASK ; p > &FIRST_TASK ; --p)
373
			if (*p && (*p)->session == current->session)
Linus Torvalds's avatar
Linus Torvalds committed
374 375 376 377 378 379 380
				(*p)->tty = -1;
	}
	if (last_task_used_math == current)
		last_task_used_math = NULL;
#ifdef DEBUG_PROC_TREE
	audit_ptree();
#endif
381 382 383 384 385
	schedule();
}

int sys_exit(int error_code)
{
Linus Torvalds's avatar
Linus Torvalds committed
386
	do_exit((error_code&0xff)<<8);
387 388
}

389
int sys_waitpid(pid_t pid,unsigned long * stat_addr, int options)
390
{
Linus Torvalds's avatar
Linus Torvalds committed
391 392 393
	int flag;
	struct task_struct *p;
	unsigned long oldblocked;
394

395 396
	if (stat_addr)
		verify_area(stat_addr,4);
397
repeat:
398
	flag=0;
Linus Torvalds's avatar
Linus Torvalds committed
399
	for (p = current->p_cptr ; p ; p = p->p_osptr) {
400
		if (pid>0) {
Linus Torvalds's avatar
Linus Torvalds committed
401
			if (p->pid != pid)
402 403
				continue;
		} else if (!pid) {
Linus Torvalds's avatar
Linus Torvalds committed
404
			if (p->pgrp != current->pgrp)
405 406
				continue;
		} else if (pid != -1) {
Linus Torvalds's avatar
Linus Torvalds committed
407
			if (p->pgrp != -pid)
408 409
				continue;
		}
Linus Torvalds's avatar
Linus Torvalds committed
410
		switch (p->state) {
411
			case TASK_STOPPED:
412 413 414
				if (!p->exit_code)
					continue;
				if (!(options & WUNTRACED) && !(p->flags & PF_PTRACED))
415
					continue;
416 417 418
				if (stat_addr)
					put_fs_long((p->exit_code << 8) | 0x7f,
						stat_addr);
Linus Torvalds's avatar
Linus Torvalds committed
419 420
				p->exit_code = 0;
				return p->pid;
421
			case TASK_ZOMBIE:
422 423 424 425
				current->cutime += p->utime + p->cutime;
				current->cstime += p->stime + p->cstime;
				current->cmin_flt += p->min_flt + p->cmin_flt;
				current->cmaj_flt += p->maj_flt + p->cmaj_flt;
Linus Torvalds's avatar
Linus Torvalds committed
426
				flag = p->pid;
427 428
				if (stat_addr)
					put_fs_long(p->exit_code, stat_addr);
Linus Torvalds's avatar
Linus Torvalds committed
429 430 431 432
				release(p);
#ifdef DEBUG_PROC_TREE
				audit_ptree();
#endif
433 434
				return flag;
			default:
435
				flag=1;
436 437 438
				continue;
		}
	}
439 440 441
	if (flag) {
		if (options & WNOHANG)
			return 0;
442
		current->state=TASK_INTERRUPTIBLE;
Linus Torvalds's avatar
Linus Torvalds committed
443 444
		oldblocked = current->blocked;
		current->blocked &= ~(1<<(SIGCHLD-1));
445
		schedule();
Linus Torvalds's avatar
Linus Torvalds committed
446 447 448
		current->blocked = oldblocked;
		if (current->signal & ~(current->blocked | (1<<(SIGCHLD-1))))
			return -ERESTARTSYS;
449
		else
Linus Torvalds's avatar
Linus Torvalds committed
450
			goto repeat;
451 452 453 454 455
	}
	return -ECHILD;
}