Projekt

Allgemein

Profil

Herunterladen (10,1 KB) Statistiken
| Zweig: | Markierung: | Revision:
/***************************************************************************
$RCSfile$
-------------------
cvs : $Id$
begin : Sat Dec 27 2003
copyright : (C) 2003 by Martin Preuss
email : martin@libchipcard.de


***************************************************************************
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Lesser General Public *
* License as published by the Free Software Foundation; either *
* version 2.1 of the License, or (at your option) any later version. *
* *
* This library 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 *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, *
* MA 02111-1307 USA *
* *
***************************************************************************/


#ifdef HAVE_CONFIG_H
# include <config.h>
#endif



#include "process_p.h"
#include <gwenhywfar/misc.h>
#include <gwenhywfar/debug.h>
#include <gwenhywfar/text.h>

#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <signal.h>


static GWEN_PROCESS *GWEN_Process_ProcessList=0;
static struct sigaction original_sigchld_sa;


GWEN_ERRORCODE GWEN_Process_ModuleInit(){
struct sigaction sa;

/* setup signal handler for SIGCHLD */
sa.sa_handler = GWEN_Process_SignalHandler;
sigemptyset(&sa.sa_mask);

sa.sa_flags = SA_SIGINFO;

/* FIXME: This needs more work! If the &sa handler is installed,
then gnucash-HEAD crashes each time on startup because guile
expected its own signal handler to be used -- the error message
is something like "in procedure waitpid: no child processes",
i.e. guile expected its own signal handler to be called for
SIGCHLD. However, when we execute this below then we get printed
"original handler was SIG_DFL". But if we call this SIG_DFL
handler below in SignalHandler, we will get a segfault. Maybe
some other flags and/or signals have to be checked? I have no
idea. */
if (sigaction(SIGCHLD, &sa, &original_sigchld_sa)) {
DBG_ERROR(0,
"Could not setup signal handler for signal SIGCHLD: %s",
strerror(errno));
return GWEN_Error_new(0,
GWEN_ERROR_SEVERITY_ERR,
0,
GWEN_ERROR_UNSPECIFIED);
}

if (original_sigchld_sa.sa_handler == SIG_DFL) {
DBG_DEBUG(0, "original_sigchld handler was SIG_DFL");
} else if (original_sigchld_sa.sa_handler == SIG_IGN) {
DBG_DEBUG(0, "original_sigchld handler was SIG_IGN");
}
else if (original_sigchld_sa.sa_flags | SA_SIGINFO) {
DBG_NOTICE(0,
"Original signal handler for signal SIGCHLD was using "
"SA_SIGINFO. ");
}

return 0;
}



GWEN_ERRORCODE GWEN_Process_ModuleFini(){
GWEN_PROCESS *pr, *prnext;

pr=GWEN_Process_ProcessList;
while(pr) {
prnext=pr->next;

pr->usage=1;
GWEN_Process_free(pr);
pr=prnext;
} /* while */
return 0;
}



GWEN_PROCESS *GWEN_Process_FindProcess(pid_t pid){
GWEN_PROCESS *pr;

pr=GWEN_Process_ProcessList;
while(pr) {
if (pr->pid==pid)
return pr;
pr=pr->next;
} /* while */
return 0;
}



void GWEN_Process_SignalHandler(int s/*, siginfo_t *siginfo, void *info*/) {
int status;
pid_t pid;

switch(s) {
case SIGCHLD:
/* try to get the status */
pid=waitpid(0, &status, WNOHANG);
if (pid==-1) {
DBG_DEBUG(0, "waitdpid(%d): %s", 0, strerror(errno));
}
else if (pid==0) {
/* process still running ?! */
DBG_DEBUG(0, "Got a SIGCHLD but no child terminated ??");
}
else {
GWEN_PROCESS *pr;

/* som process terminated */
pr=GWEN_Process_FindProcess(pid);
if (!pr) {
DBG_NOTICE(0, "No infomation about process \"%d\" available", pid);
}
else {
GWEN_Process_MakeState(pr, status);
/* remove from list. If this process data is not used by the
* aplication it will now be freed, otherwise only the usage
* counter is decremented */
GWEN_Process_free(pr);
}
}
break;

default:
DBG_ERROR(0, "Got unhandled signal \"%d\"", s);
break;
} /* switch */

/* Now check whether there has been already a different signal
handler for this signal */
if ( (original_sigchld_sa.sa_handler != SIG_DFL) &&
(original_sigchld_sa.sa_handler != SIG_IGN) ) {
if (original_sigchld_sa.sa_flags | SA_SIGINFO) {
DBG_NOTICE(0, "Unimplemented: About to call original sa_sigaction at signal \"%d\"", s);
/*original_sigchld_sa.sa_sigaction(s, siginfo, info);*/
} else {
DBG_NOTICE(0, "About to call original sa_handler at signal \"%d\"", s);
original_sigchld_sa.sa_handler(s);
}
}

}



GWEN_PROCESS *GWEN_Process_new(){
GWEN_PROCESS *pr;

GWEN_NEW_OBJECT(GWEN_PROCESS, pr);
pr->state=GWEN_ProcessStateNotStarted;
pr->pid=-1;
pr->usage=1;
GWEN_LIST_ADD(GWEN_PROCESS, pr, &GWEN_Process_ProcessList);
return pr;
}



void GWEN_Process_free(GWEN_PROCESS *pr){
if (pr) {
assert(pr->usage);
if (pr->usage==1) {
/* unlink from list */
GWEN_LIST_DEL(GWEN_PROCESS, pr, &GWEN_Process_ProcessList);
free(pr);
}
else
pr->usage--;
}
}



GWEN_PROCESS_STATE GWEN_Process_Start(GWEN_PROCESS *pr,
const char *prg,
const char *args){
pid_t pid;
char buffer[128];
char *argv[32];
int argc;
const char *p, *p2;

assert(pr);

pid=fork();
if (pid==-1) {
/* error in fork */
pr->state=GWEN_ProcessStateNotStarted;
pr->pid=-1;
return GWEN_ProcessStateNotStarted;
}
else if (pid!=0) {
/* parent */
pr->state=GWEN_ProcessStateRunning;
pr->pid=pid;
return GWEN_ProcessStateRunning;
}
/* child, build arguments */
argc=0;

argv[0]=strdup(prg);
argc++;
p=args;
while(argc<32 && *p) {
while(*p && isspace(*p))
p++;
if (!(*p))
break;
p2=GWEN_Text_GetWord(p, " ",
buffer, sizeof(buffer),
GWEN_TEXT_FLAGS_NULL_IS_DELIMITER |
GWEN_TEXT_FLAGS_DEL_QUOTES |
GWEN_TEXT_FLAGS_CHECK_BACKSLASH,
&p);
if (!p2)
break;

argv[argc]=strdup(p2);
argc++;
} /* while */
argv[argc]=0;
/* parameters ready, exec */
execvp(prg, argv);
/* if we reach this point an error occurred */
DBG_ERROR(0, "Could not start program \"%s\": %s",
prg, strerror(errno));
exit(EXIT_FAILURE);
}



GWEN_PROCESS_STATE GWEN_Process_GetState(GWEN_PROCESS *pr, int w){
int rv;
int status;

assert(pr);

/* try to get the status */
rv=waitpid(pr->pid, &status, w?0:WNOHANG);
if (rv==-1) {
DBG_ERROR(0, "waitdpid(%d): %s", pr->pid, strerror(errno));
return GWEN_ProcessStateUnknown;
}
else if (rv==0) {
/* process still running */
return GWEN_ProcessStateRunning;
}
else {
return GWEN_Process_MakeState(pr, status);
}
}



GWEN_PROCESS_STATE GWEN_Process_MakeState(GWEN_PROCESS *pr, int status){
/* process has terminated for any reason */
if (WIFEXITED(status)) {
/* normal termination */
DBG_INFO(0, "Process %d exited with %d",
pr->pid, WEXITSTATUS(status));
pr->state=GWEN_ProcessStateExited;
pr->pid=-1;
/* store result code */
pr->result=WEXITSTATUS(status);
return pr->state;
} /* if exited normally */
else if (WIFSIGNALED(status)) {
/* uncaught signal */
DBG_ERROR(0, "Process %d terminated by signal %d",
pr->pid, WTERMSIG(status));
pr->state=GWEN_ProcessStateAborted;
pr->pid=-1;
return pr->state;
} /* if terminated by signal */
else if (WIFSTOPPED(status)) {
/* process stopped by signal */
DBG_ERROR(0, "Process %d stopped by signal %d",
pr->pid, WSTOPSIG(status));
pr->state=GWEN_ProcessStateStopped;
pr->pid=-1;
return pr->state;
}
else {
DBG_ERROR(0, "Unhandled status, assume process %d isn't running (%08x)",
pr->pid, (unsigned int)status);
return GWEN_ProcessStateUnknown;
}
}



GWEN_PROCESS_STATE GWEN_Process_CheckState(GWEN_PROCESS *pr){
assert(pr);

if (pr->pid==-1)
/* we already know the state, return it */
return pr->state;

/* otherwise try to get the status */
return GWEN_Process_GetState(pr, 0);
}



int GWEN_Process_GetResult(GWEN_PROCESS *pr){
assert(pr);
if (GWEN_Process_CheckState(pr)==GWEN_ProcessStateExited)
return pr->result;
else
return -1;
}



int GWEN_Process_Wait(GWEN_PROCESS *pr){
GWEN_PROCESS_STATE pst;

assert(pr);

if (pr->state!=GWEN_ProcessStateRunning)
/* process is not running, so return */
return 0;

if (pr->pid==-1) {
/* process is running, but we have no pid ?! */
DBG_ERROR(0, "Process is running but we don't have its pid");
return -1;
}
pst=GWEN_Process_GetState(pr, 1);
if (pst==GWEN_ProcessStateUnknown)
return -1;
return 0;
}



int GWEN_Process_Terminate(GWEN_PROCESS *pr){
assert(pr);

if (pr->state!=GWEN_ProcessStateRunning) {
/* process is not running, so return */
DBG_INFO(0, "Process is not running, doing nothing");
return 0;
}

if (pr->pid==-1) {
/* process is running, but we have no pid ?! */
DBG_ERROR(0, "Process is running but we don't have its pid");
return -1;
}

/* kill process */
if (kill(pr->pid, SIGKILL)) {
DBG_ERROR(0, "Error on kill(%d, SIGKILL): %s",
pr->pid, strerror(errno));
return -1;
}
/* wait for process to respond to kill signal (should not take long) */
return GWEN_Process_Wait(pr);
}


























(13-13/15)