mirror of
https://github.com/samuelclay/NewsBlur.git
synced 2025-08-31 21:41:33 +00:00
271 lines
6.8 KiB
C
271 lines
6.8 KiB
C
/*
|
|
* $Id: process_info.c 1462 2012-07-18 03:12:08Z g.rodola $
|
|
*
|
|
* Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.
|
|
* Use of this source code is governed by a BSD-style license that can be
|
|
* found in the LICENSE file.
|
|
*
|
|
* Helper functions related to fetching process information. Used by _psutil_bsd
|
|
* module methods.
|
|
*/
|
|
|
|
#include <Python.h>
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <sys/types.h>
|
|
#include <sys/sysctl.h>
|
|
#include <sys/param.h>
|
|
#include <sys/user.h>
|
|
#include <sys/proc.h>
|
|
#include <signal.h>
|
|
|
|
#include "process_info.h"
|
|
|
|
|
|
/*
|
|
* Returns a list of all BSD processes on the system. This routine
|
|
* allocates the list and puts it in *procList and a count of the
|
|
* number of entries in *procCount. You are responsible for freeing
|
|
* this list (use "free" from System framework).
|
|
* On success, the function returns 0.
|
|
* On error, the function returns a BSD errno value.
|
|
*/
|
|
int
|
|
get_proc_list(struct kinfo_proc **procList, size_t *procCount)
|
|
{
|
|
int err;
|
|
struct kinfo_proc * result;
|
|
int done;
|
|
static const int name[] = { CTL_KERN, KERN_PROC, KERN_PROC_PROC, 0 };
|
|
// Declaring name as const requires us to cast it when passing it to
|
|
// sysctl because the prototype doesn't include the const modifier.
|
|
size_t length;
|
|
|
|
assert( procList != NULL);
|
|
assert(*procList == NULL);
|
|
assert(procCount != NULL);
|
|
|
|
*procCount = 0;
|
|
|
|
/*
|
|
* We start by calling sysctl with result == NULL and length == 0.
|
|
* That will succeed, and set length to the appropriate length.
|
|
* We then allocate a buffer of that size and call sysctl again
|
|
* with that buffer. If that succeeds, we're done. If that fails
|
|
* with ENOMEM, we have to throw away our buffer and loop. Note
|
|
* that the loop causes use to call sysctl with NULL again; this
|
|
* is necessary because the ENOMEM failure case sets length to
|
|
* the amount of data returned, not the amount of data that
|
|
* could have been returned.
|
|
*/
|
|
result = NULL;
|
|
done = 0;
|
|
do {
|
|
assert(result == NULL);
|
|
// Call sysctl with a NULL buffer.
|
|
length = 0;
|
|
err = sysctl((int *)name, (sizeof(name) / sizeof(*name)) - 1,
|
|
NULL, &length, NULL, 0);
|
|
if (err == -1)
|
|
err = errno;
|
|
|
|
// Allocate an appropriately sized buffer based on the results
|
|
// from the previous call.
|
|
if (err == 0) {
|
|
result = malloc(length);
|
|
if (result == NULL)
|
|
err = ENOMEM;
|
|
}
|
|
|
|
// Call sysctl again with the new buffer. If we get an ENOMEM
|
|
// error, toss away our buffer and start again.
|
|
if (err == 0) {
|
|
err = sysctl((int *) name, (sizeof(name) / sizeof(*name)) - 1,
|
|
result, &length, NULL, 0);
|
|
if (err == -1)
|
|
err = errno;
|
|
if (err == 0) {
|
|
done = 1;
|
|
}
|
|
else if (err == ENOMEM) {
|
|
assert(result != NULL);
|
|
free(result);
|
|
result = NULL;
|
|
err = 0;
|
|
}
|
|
}
|
|
} while (err == 0 && ! done);
|
|
|
|
// Clean up and establish post conditions.
|
|
if (err != 0 && result != NULL) {
|
|
free(result);
|
|
result = NULL;
|
|
}
|
|
|
|
*procList = result;
|
|
*procCount = length / sizeof(struct kinfo_proc);
|
|
|
|
assert((err == 0) == (*procList != NULL));
|
|
return err;
|
|
}
|
|
|
|
|
|
char
|
|
*getcmdpath(long pid, size_t *pathsize)
|
|
{
|
|
int mib[4];
|
|
char *path;
|
|
size_t size = 0;
|
|
|
|
/*
|
|
* Make a sysctl() call to get the raw argument space of the process.
|
|
*/
|
|
mib[0] = CTL_KERN;
|
|
mib[1] = KERN_PROC;
|
|
mib[2] = KERN_PROC_PATHNAME;
|
|
mib[3] = pid;
|
|
|
|
// call with a null buffer first to determine if we need a buffer
|
|
if (sysctl(mib, 4, NULL, &size, NULL, 0) == -1) {
|
|
return NULL;
|
|
}
|
|
|
|
path = malloc(size);
|
|
if (path == NULL) {
|
|
PyErr_SetString(PyExc_MemoryError, "couldn't allocate memory");
|
|
return NULL;
|
|
}
|
|
|
|
*pathsize = size;
|
|
if (sysctl(mib, 4, path, &size, NULL, 0) == -1) {
|
|
free(path);
|
|
return NULL; /* Insufficient privileges */
|
|
}
|
|
|
|
return path;
|
|
}
|
|
|
|
|
|
/*
|
|
* Borrowed from psi Python System Information project
|
|
*
|
|
* Get command arguments and environment variables.
|
|
*
|
|
* Based on code from ps.
|
|
*
|
|
* Returns:
|
|
* 0 for success;
|
|
* -1 for failure (Exception raised);
|
|
* 1 for insufficient privileges.
|
|
*/
|
|
char
|
|
*getcmdargs(long pid, size_t *argsize)
|
|
{
|
|
int mib[4];
|
|
size_t size, argmax;
|
|
char *procargs = NULL;
|
|
|
|
/* Get the maximum process arguments size. */
|
|
mib[0] = CTL_KERN;
|
|
mib[1] = KERN_ARGMAX;
|
|
|
|
size = sizeof(argmax);
|
|
if (sysctl(mib, 2, &argmax, &size, NULL, 0) == -1)
|
|
return NULL;
|
|
|
|
/* Allocate space for the arguments. */
|
|
procargs = (char *)malloc(argmax);
|
|
if (procargs == NULL) {
|
|
PyErr_SetString(PyExc_MemoryError, "couldn't allocate memory");
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Make a sysctl() call to get the raw argument space of the process.
|
|
*/
|
|
mib[0] = CTL_KERN;
|
|
mib[1] = KERN_PROC;
|
|
mib[2] = KERN_PROC_ARGS;
|
|
mib[3] = pid;
|
|
|
|
size = argmax;
|
|
if (sysctl(mib, 4, procargs, &size, NULL, 0) == -1) {
|
|
free(procargs);
|
|
return NULL; /* Insufficient privileges */
|
|
}
|
|
|
|
// return string and set the length of arguments
|
|
*argsize = size;
|
|
return procargs;
|
|
}
|
|
|
|
|
|
/* returns the command line as a python list object */
|
|
PyObject*
|
|
get_arg_list(long pid)
|
|
{
|
|
char *argstr = NULL;
|
|
int pos = 0;
|
|
size_t argsize = 0;
|
|
PyObject *retlist = Py_BuildValue("[]");
|
|
PyObject *item = NULL;
|
|
|
|
if (pid < 0) {
|
|
return retlist;
|
|
}
|
|
|
|
argstr = getcmdargs(pid, &argsize);
|
|
if (argstr == NULL) {
|
|
goto error;
|
|
}
|
|
|
|
// args are returned as a flattened string with \0 separators between
|
|
// arguments add each string to the list then step forward to the next
|
|
// separator
|
|
if (argsize > 0) {
|
|
while(pos < argsize) {
|
|
item = Py_BuildValue("s", &argstr[pos]);
|
|
if (!item)
|
|
goto error;
|
|
if (PyList_Append(retlist, item))
|
|
goto error;
|
|
Py_DECREF(item);
|
|
pos = pos + strlen(&argstr[pos]) + 1;
|
|
}
|
|
}
|
|
|
|
free(argstr);
|
|
return retlist;
|
|
|
|
error:
|
|
Py_XDECREF(item);
|
|
Py_DECREF(retlist);
|
|
if (argstr != NULL)
|
|
free(argstr);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/*
|
|
* Return 1 if PID exists in the current process list, else 0.
|
|
*/
|
|
int
|
|
pid_exists(long pid)
|
|
{
|
|
int kill_ret;
|
|
if (pid < 0) {
|
|
return 0;
|
|
}
|
|
|
|
// if kill returns success of permission denied we know it's a valid PID
|
|
kill_ret = kill(pid , 0);
|
|
if ((0 == kill_ret) || (EPERM == errno)) {
|
|
return 1;
|
|
}
|
|
|
|
// otherwise return 0 for PID not found
|
|
return 0;
|
|
}
|