/* * Copyright (c) 1996 Gunther Schadow. All rights reserved. * * 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 2 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, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #pragma implementation #include "Proc.h" #ifdef PROTOGEN # include "exception.h" # include "logfile.h" #else # include # define ERROS(fmt, args...) { fprintf(stderr, fmt ": " , ## args); \ perror(""); exit(1); } # define ERROR(fmt, args...) { fprintf(stderr, fmt "\n" , ## args); exit(1); } #endif #include #include "Signal.h" Signal ProcInfo::_sigchld(SIGCHLD, ProcInfo::reaper, Signal::f_restart, Signal::f_disable); ProcInfo *ProcInfo::_root_proc = NULL; ProcInfo::ProcInfo(int flags) { _flags = flags & user_mask; _wstatus = 0; _pid = -1; // log on to the process info list bool b = _sigchld.block(); // no SIGCHLD! _next_proc = _root_proc; _root_proc = this; _prev_proc = NULL; if(_next_proc != NULL) _next_proc->_prev_proc = this; _sigchld.block(b); } ProcInfo::~ProcInfo() { bool b = _sigchld.block(); if(parent() && (_flags & join)) { if(_flags & ProcInfo::term) ::kill(_pid, SIGTERM); if(_flags & ProcInfo::kill) ::kill(_pid, SIGKILL); int status; waitpid(_pid, &status, 0); // must be reaped now! } // log off the process info list if(_next_proc != NULL) _next_proc->_prev_proc = _prev_proc; if(_prev_proc != NULL) _prev_proc->_next_proc = _next_proc; else _root_proc = NULL; _sigchld.block(b); } int ProcInfo::reaper(Signal &) { int wstatus; ProcInfo *p = _root_proc; while(p != NULL) { if((p->_flags & dead) && (p->_flags & clean)) { ProcInfo *q = p->_next_proc; delete p; p = q; } else { if(waitpid(p->_pid, &wstatus, WNOHANG) == p->_pid) { if(p->_flags & clean) { ProcInfo *q = p->_next_proc; delete p; p = q; } else { p->_flags |= dead; p->_wstatus = wstatus; p = p->_next_proc; } } else p = p->_next_proc; } } // you never know how many processes have died, thus, we return // control to the next SIGCHLD handler return 0; } Proc::Proc(int flags) { _info = new ProcInfo(flags & ProcInfo::user_mask); if(flags & ProcInfo::now) run(); } Proc::~Proc() { if(parent()) if(_info->_flags & ProcInfo::join) { delete _info; } else { _info->_flags |= ProcInfo::clean; } else // neither child nor anything else uses ProcInfo without Proc delete _info; } void Proc::run() { if(! started()) { _info->_pid = ::fork(); if(_info->_pid == -1) ERROS("can't fork"); if(_info->_pid == 0) // child { _info->_flags |= ProcInfo::start | ProcInfo::chld; } else // parent { _info->_flags = _info->_flags & ~ProcInfo::chld | ProcInfo::start; } } else ERROR("process is already started"); } void Proc::vrun(vinit_t vinit, ...) { va_list ap; va_start(ap, vinit); if(! started()) { _info->_pid = ::vfork(); if(_info->_pid == -1) ERROS("can't fork"); if(_info->_pid == 0) // child { (*vinit)(ap); _exit(1); // we should never come back! } else // parent { _info->_flags = _info->_flags & ~ProcInfo::chld | ProcInfo::start; } } else ERROR("process is already started"); va_end(ap); }