#include #include #include #include #include #include #include #include #include #include "netconf.h" #include "netconf.m" #include "daemoni.h" /* Read the first line of a file Return -1 if any error. */ static int process_readfile (const char *path, char *buf, int sizebuf) { int ret = -1; // No error message signaled as "path" is a file under /proc/pid // and the process may vanish while we are reading it. FILE *fin = fopen (path,"r"); if (fin != NULL){ if (fgets (buf,sizebuf-1,fin)!=NULL){ ret = 0; } fclose (fin); } return ret; } struct PS_PROC { char cmdline[256], user[10], cmd[40], state, ttyc[4]; int uid, pid, ppid, pgrp, session, tty, tpgid, utime, stime, cutime, cstime, counter, priority, start_time, signal, blocked, sigignore, sigcatch; unsigned int flags, min_flt, cmin_flt, maj_flt, cmaj_flt, timeout, it_real_value, vsize, rss, rss_rlim, start_code, end_code, start_stack, kstk_esp, kstk_eip, wchan; }; /* Read the file /proc/pid/stat into the struct PS_PROC Return -1 if any error. */ static int process_readstat(int pid, PS_PROC &_psp) { int ret = -1; char tmppath[PATH_MAX]; sprintf (tmppath,"/proc/%d/stat",pid); char buf[PATH_MAX]; if (process_readfile (tmppath,buf,sizeof(buf))!=-1){ PS_PROC psp; sscanf(buf, "%d %s %c %d %d %d %d %d %u %u " "%u %u %u %d %d %d %d %d %d %u " "%u %d %u %u %u %u %u %u %u %u %u " "%u %u %u %u\n", &psp.pid, psp.cmd, &psp.state, &psp.ppid, &psp.pgrp, &psp.session, &psp.tty, &psp.tpgid, &psp.flags, &psp.min_flt, &psp.cmin_flt, &psp.maj_flt, &psp.cmaj_flt, &psp.utime, &psp.stime, &psp.cutime, &psp.cstime, &psp.counter, &psp.priority, &psp.timeout, &psp.it_real_value, &psp.start_time, &psp.vsize, &psp.rss, &psp.rss_rlim, &psp.start_code, &psp.end_code, &psp.start_stack, &psp.kstk_esp, &psp.kstk_eip, &psp.signal, &psp.blocked, &psp.sigignore, &psp.sigcatch, &psp.wchan); _psp = psp; ret = 0; } return ret; } /* Information about a process ID */ PUBLIC PROC::PROC (int _pid, PROC *_next) { next = _next; pid = _pid; ppid = -1; path = NULL; name = NULL; char tmppath[PATH_MAX]; sprintf (tmppath,"/proc/%d/cmdline",_pid); char buf[PATH_MAX]; PS_PROC psp; if (process_readfile (tmppath,buf,sizeof(buf))!=-1){ if (sscanf (buf,"%s",tmppath)==1){ // Small patch for sendmail char *pt = strchr(tmppath,':'); if (pt != NULL) *pt = '\0'; path = strdup(tmppath); } } if (process_readstat(_pid,psp)!=-1){ strcpy (buf,psp.cmd[0] == '(' ? psp.cmd+1 : psp.cmd); char *pt = strchr(buf,')'); if (pt != NULL) *pt = '\0'; path = strdup(buf); ppid = psp.ppid; } if (path != NULL){ name = strrchr(path,'/'); if (name != NULL){ name++; }else{ name = path; } } } PUBLIC PROC::~PROC () { free (path); } /* Return the next PROC in the list or NULL. */ PUBLIC PROC *PROC::getnext () { return next; } /* Return != 0 if the information on the process was read properly. */ PUBLIC int PROC::isok () { return path != NULL; } /* Return the path (command line) of a process */ PUBLIC const char *PROC::getpath () { return path; } /* Return the name (command line) of a process */ PUBLIC const char *PROC::getname () { return name; } /* Send a signal to the process. */ PUBLIC int PROC::kill (int signo) { int ret = 0; if (!simul_ison()){ ret = ::kill (pid,signo); } return ret; } PUBLIC int PROC::getpid() { return pid; } PUBLIC int PROC::getppid() { return ppid; } /* Return the start time of a process. Return -1 is the process is dead. */ PUBLIC long PROC::getstarttime () { long ret = -1; long uptime; char buf[200]; if (process_readfile ("/proc/uptime",buf,sizeof(buf))!=-1){ uptime = atoi(buf); PS_PROC psp; if (process_readstat(pid,psp)!=-1){ long seconds = uptime - (psp.start_time / HZ); ret = time(NULL) - seconds; } } return ret; } static PROC *first = NULL; void process_flushcache() { while (first != NULL){ PROC *next = first->getnext(); delete first; first = next; } } /* Read all the process currently active and store for usage by other process_function. The info is kept for all process_find and isalive call. It is flushed when process_read is called again. */ int process_read() { process_flushcache(); SSTRINGS lst; int nbproc = 0; int n = dir_getlist ("/proc",lst); for (int i=0; iget(); int pid = atoi(dirpid); if (pid != 0){ PROC *prc = new PROC (pid,first); if (prc->isok()){ first = prc; nbproc++; }else{ delete prc; } } } return nbproc; } static PROC *process_find(const char *path, const char *name, int pid) { PROC *ret = first; while (ret != NULL){ const char *ppath = ret->getpath(); const char *pname = ret->getname(); if (pid != -1){ if (ret->getpid() == pid) break; }else if (strcmp(path,ppath)==0 || strcmp(name,pname)==0){ break; } ret = ret->getnext(); } return ret; } /* Check if a process is running at least once Return -1 or the start time of this process. */ EXPORT PROC *process_find ( const char *path, const CONFIG_FILE *pidfile) // May be NULL { /* #Specification: process / lookup / strategy When looking for a process, we search first for the exact path (command line). If this fail, then we use the name only without path. This should be safe enough to prevent mistake. If there is a PID file (/var/run/sendmail.pid), we use this instead. If there is no PID file, we assume there is no process. At some point, linuxconf used to be smart and try to lookup the process anyway (by name). This breaks the case where several instance of the same process name exists independantly (the kernel automounter damon for one) with different pid files. */ if (first == NULL) process_read (); PROC *ret = NULL; if (pidfile != NULL){ FILE_CFG *fin = pidfile->fopen("r"); if (fin != NULL){ int pid; if (fscanf(fin,"%d\n",&pid)==1){ ret = process_find (path,"",pid); }else{ xconf_error (MSG_U(E_PIDFILE,"Invalid PID file: %s") ,pidfile->getpath()); } fclose (fin); } }else{ const char *name = strrchr (path,'/'); name = name == NULL ? path : name + 1; ret = process_find (path,"",-1); if (ret == NULL) ret = process_find ("",name,-1); } return ret; } /* Find the child (the first) of a process. Return -1 if none. */ EXPORT int process_findchild (int pid) { process_read (); int ret = -1; PROC *p = first; while (p != NULL){ if (p->getppid() == pid){ ret = p->getpid(); break; }else{ p = p->getnext(); } } return ret; } /* Check if a process is running at least once Return -1 or the start time of this process. */ EXPORT long process_isactive (const char *path) { PROC *prc = process_find (path,NULL); long ret = -1; if (prc != NULL) ret = prc->getstarttime(); return ret; } #ifdef TEST int main (int argc, char *argv[]) { for (int i=1; i ",argv[i]); if (start == -1){ printf ("nil\n"); }else{ printf ("%ld %s\n",start,ctime(&start)); } } return 0; } #endif