/* Copyright (C) 2008 Emmanuel Varoquaux
This file is part of XOS.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see . */
#include "make_status.h"
#include "status.h"
#include "signal.h"
#include "sched.h"
#include "proc.h"
#include "alloc_proc.h"
#include "idle.h"
#include "proc_struct.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
/* Groupe de processus orphelin */
/* A process group is considered to be orphaned if it is not under the control
* of a job control shell. More precisely, a process group is orphaned when
* none of its members has a parent process that is in the same session as the
* group, but is in a different process group. Note that when a process exits,
* the parent process for its children is changed to be init, which is in a
* separate session. Not all members of an orphaned process group are
* necessarily orphaned processes (those whose creating process has exited).
* The process group of a session leader is orphaned by definition. */
/* Dit si un processus contribue a ce que son groupe devienne orphelin,
* c'est-a-dire s'il n'est pas sous controle d'un processus d'un autre groupe.
* Peut etre legitimement appele si proc est zombie. */
static int is_orphaned(const struct proc_struct *proc)
{
return is_orphan(proc) || proc->parent->group == proc->group;
}
/* Dit si un groupe de processus est orphelin. */
int is_orphaned_proc_group(const struct proc_group_struct *proc_group)
{
struct proc_struct *proc;
for (proc = proc_group->first_proc; proc; proc = proc->group_next) {
if (proc->state == PS_ZOMBIE)
continue; /* les processus zombies sont ignores */
if (!is_orphaned(proc))
return 0;
}
return 1;
}
/* Dit si le groupe d'un processus devient orphelin si le processus devient
* zombie ou orphelin. */
static int become_orphaned_proc_group(const struct proc_struct *proc)
{
struct proc_struct *p;
if (is_orphaned(proc))
return 0;
for (p = proc->group->first_proc; p; p = p->group_next) {
if (p == proc)
continue;
if (p->state == PS_ZOMBIE)
continue; /* les processus zombies sont ignores */
if (!is_orphaned(p))
return 0;
}
return 1;
}
/* Dit si un groupe de processus a des membres stoppes. */
static int has_stopped_member(const struct proc_group_struct *proc_group)
{
struct proc_struct *proc;
for (proc = proc_group->first_proc; proc; proc = proc->group_next)
if (proc->state == PS_STOPPED)
return 1;
return 0;
}
/* Terminaison du processus courant */
static void close_file_descrs(struct file_descr_struct *file_descr_table[])
{
struct file_descr_struct **file_descr;
for (file_descr = file_descr_table; file_descr < file_descr_table + OPEN_MAX; file_descr++)
if (*file_descr)
fd_close(*file_descr);
}
static void hangup_newly_orphaned_process_group(const struct proc_struct *proc)
{
if (!become_orphaned_proc_group(proc))
return;
if (has_stopped_member(proc->group)) {
signal_group(proc->group, SIGHUP);
signal_group(proc->group, SIGCONT);
}
}
/* Si la fin du processus rend orphelin un groupe de processus, et si certain
* membres de ce groupe sont arretes, alors tous les processus du groupe vont
* recevoir SIGHUP suivi de SIGCONT. */
static void __attribute__ ((noreturn)) terminate(int status)
{
struct proc_struct *child;
cli();
/* liberation des ressources */
sched_remove(current);
close_file_descrs(current->file_descr_table);
vm_free(¤t->vm);
node_release(current->wd);
string_table_destroy(¤t->cmdline);
/* passage dans l'etat ZOMBIE et deconnection de l'arborescence */
current->state = PS_ZOMBIE;
/* On regarde si le groupe du processus devient orphelin, ce qui est le cas
si le processus etait le dernier encore sous controle. */
hangup_newly_orphaned_process_group(current);
/* On regarde si le groupe de chaque fils devient orphelin, ce qui est le cas
si le processus etait le dernier qui controlait un membre de ce groupe. */
for (child = current->first_child; child; child = child->next_sibling)
hangup_newly_orphaned_process_group(child);
proc_tree_dishinerit_children(current);
set_status(status);
/* schedule */
schedule();
while (1)
hlt();
}
/* Terminaison normale, demandee par le processus lui-meme. */
void __attribute__ ((noreturn)) exit(int code)
{
terminate(make_status(0, code, 0));
}
/* Terminaison anormale, provoquee par une faute du processus ou un signal. */
void __attribute__ ((noreturn)) abort(int sig)
{
terminate(make_status(sig, 0, 0));
}
/* Attente de la fin d'un processus */
static void destroy_proc(struct proc_struct *proc)
{
proc_group_remove(proc);
proc_tree_remove(proc);
proc_list_remove(proc);
proc_table_remove(proc);
}
/* Detruit et libere definitivement un processus. */
static void discard_proc(struct proc_struct *proc)
{
destroy_proc(proc);
free_proc(proc);
}
/* Recoit l'information d'un fils s'il a change d'etat.
* Libere les ressources du fils si celui-ci est termine.
* Retourne le pid du fils en cas de succes, 0 en cas d'echec. */
static int reap(struct proc_struct *child, int *status, int options)
{
int pid;
if (!report_status(child, status))
return 0; /* information non disponible */
pid = child->id;
switch (child->state) {
case PS_STOPPED:
if (!(options & WUNTRACED))
return 0;
break;
case PS_ZOMBIE:
discard_proc(child);
break;
default:
return 0;
}
return pid;
}
static int reap_any(struct proc_struct *child, struct proc_group_struct *proc_group, int *status, int options)
{
int pid;
int child_exists;
if (child)
return reap(child, status, options);
child_exists = 0;
for (child = current->first_child; child; child = child->next_sibling) {
if (proc_group && child->group != proc_group)
continue;
child_exists = 1;
if ((pid = reap(child, status, options)))
return pid;
}
return child_exists ? 0 : -ECHILD;
}
static int do_wait(struct proc_struct *child, struct proc_group_struct *proc_group, int *status, int options)
{
int retval;
int intr;
disable_intr(intr);
while (!(retval = reap_any(child, proc_group, status, options))) {
if (options & WNOHANG) {
retval = 0;
goto restore;
}
if (!wait_child_status()) {
retval = -ERESTARTSYS;
goto restore;
}
}
restore:
restore_intr(intr);
return retval;
}
int wait(struct proc_struct *child, int *status, int options)
{
return do_wait(child, NULL, status, options);
}
int wait_group_any(struct proc_group_struct *proc_group, int *status, int options)
{
return do_wait(NULL, proc_group, status, options);
}
int wait_any(int *status, int options)
{
return do_wait(NULL, NULL, status, options);
}
void reap_orphan_zombies()
{
int intr;
struct proc_struct *orphan, *next;
disable_intr(intr);
if (idle->first_child) {
for (orphan = idle->first_child; orphan; orphan = next) {
next = orphan->next_sibling;
if (orphan->state == PS_ZOMBIE)
discard_proc(orphan);
}
if (!idle->first_child) /* on a detruit init */
check_paging_empty(); /* test de consistence */
}
restore_intr(intr);
}