OLD | NEW |
1 /* | 1 /* |
2 * $Id: process_info.c 772 2010-11-03 13:51:11Z g.rodola $ | 2 * $Id: process_info.c 1142 2011-10-05 18:45:49Z g.rodola $ |
| 3 * |
| 4 * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved. |
| 5 * Use of this source code is governed by a BSD-style license that can be |
| 6 * found in the LICENSE file. |
3 * | 7 * |
4 * Helper functions related to fetching process information. Used by _psutil_osx | 8 * Helper functions related to fetching process information. Used by _psutil_osx |
5 * module methods. | 9 * module methods. |
6 */ | 10 */ |
7 | 11 |
8 #include <Python.h> | 12 #include <Python.h> |
9 #include <assert.h> | 13 #include <assert.h> |
10 #include <errno.h> | 14 #include <errno.h> |
11 #include <limits.h> /* for INT_MAX */ | 15 #include <limits.h> /* for INT_MAX */ |
12 #include <stdbool.h> | 16 #include <stdbool.h> |
13 #include <stdlib.h> | 17 #include <stdlib.h> |
14 #include <stdio.h> | 18 #include <stdio.h> |
| 19 #include <signal.h> |
15 #include <sys/sysctl.h> | 20 #include <sys/sysctl.h> |
16 | 21 |
17 #include "process_info.h" | 22 #include "process_info.h" |
| 23 #include "../../_psutil_common.h" |
18 | 24 |
19 #define ARGS_ACCESS_DENIED -2 | 25 |
| 26 /* |
| 27 * Return 1 if PID exists in the current process list, else 0. |
| 28 */ |
| 29 int |
| 30 pid_exists(long pid) |
| 31 { |
| 32 int kill_ret; |
| 33 |
| 34 // save some time if it's an invalid PID |
| 35 if (pid < 0) { |
| 36 return 0; |
| 37 } |
| 38 |
| 39 // if kill returns success of permission denied we know it's a valid PID |
| 40 kill_ret = kill(pid , 0); |
| 41 if ( (0 == kill_ret) || (EPERM == errno) ) { |
| 42 return 1; |
| 43 } |
| 44 |
| 45 // otherwise return 0 for PID not found |
| 46 return 0; |
| 47 } |
20 | 48 |
21 | 49 |
22 | 50 |
23 /* | 51 /* |
24 * Returns a list of all BSD processes on the system. This routine | 52 * Returns a list of all BSD processes on the system. This routine |
25 * allocates the list and puts it in *procList and a count of the | 53 * allocates the list and puts it in *procList and a count of the |
26 * number of entries in *procCount. You are responsible for freeing | 54 * number of entries in *procCount. You are responsible for freeing |
27 * this list (use "free" from System framework). | 55 * this list (use "free" from System framework). |
28 * On success, the function returns 0. | 56 * On success, the function returns 0. |
29 * On error, the function returns a BSD errno value. | 57 * On error, the function returns a BSD errno value. |
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
87 } else { | 115 } else { |
88 *procList = (kinfo_proc *)ptr; | 116 *procList = (kinfo_proc *)ptr; |
89 *procCount = size / sizeof(kinfo_proc); | 117 *procCount = size / sizeof(kinfo_proc); |
90 return 0; | 118 return 0; |
91 } | 119 } |
92 } | 120 } |
93 return ENOMEM; | 121 return ENOMEM; |
94 } | 122 } |
95 | 123 |
96 | 124 |
97 /* | 125 /* Read the maximum argument size for processes */ |
98 * Modified from psi Python System Information project | |
99 * | |
100 * Get command path, arguments and environment variables. | |
101 * | |
102 * Based on code from ps. | |
103 * | |
104 * Returns: | |
105 * 0 for success | |
106 * 1 for sysctl error, errno exception raised | |
107 * -1 for failure, system or memory exception raised | |
108 * -2 rather ARGS_ACCESS_DENIED, for insufficient privileges | |
109 */ | |
110 int | 126 int |
111 getcmdargs(long pid, PyObject **exec_path, PyObject **envlist, PyObject **arglis
t) | 127 get_argmax() |
112 { | 128 { |
113 int nargs, mib[3]; | 129 int argmax; |
114 size_t size, argmax; | 130 int mib[] = { CTL_KERN, KERN_ARGMAX }; |
115 char *curr_arg, *start_args, *iter_args, *end_args; | 131 size_t size = sizeof(argmax); |
116 char *procargs = NULL; | |
117 char *err = NULL; | |
118 PyObject *arg; | |
119 | 132 |
120 *arglist = Py_BuildValue("[]"); /* empty list */ | 133 if (sysctl(mib, 2, &argmax, &size, NULL, 0) == 0) { |
121 if (*arglist == NULL) { | 134 return argmax; |
122 err = "getcmdargs(): arglist exception"; | |
123 goto ERROR_RETURN; | |
124 } | |
125 | |
126 /* Get the maximum process arguments size. */ | |
127 mib[0] = CTL_KERN; | |
128 mib[1] = KERN_ARGMAX; | |
129 | |
130 size = sizeof(argmax); | |
131 if (sysctl(mib, 2, &argmax, &size, NULL, 0) == -1) { | |
132 PyErr_SetFromErrno(NULL); | |
133 return errno; | |
134 } | |
135 | |
136 /* Allocate space for the arguments. */ | |
137 procargs = (char *)malloc(argmax); | |
138 if (procargs == NULL) { | |
139 PyErr_SetString(PyExc_MemoryError, | |
140 "getcmdargs(): insufficient memory for procargs"); | |
141 return ENOMEM; | |
142 } | |
143 | |
144 /* | |
145 * Make a sysctl() call to get the raw argument space of the process. | |
146 */ | |
147 mib[0] = CTL_KERN; | |
148 mib[1] = KERN_PROCARGS2; | |
149 mib[2] = (int)pid; | |
150 | |
151 size = argmax; | |
152 if (sysctl(mib, 3, procargs, &size, NULL, 0) == -1) { | |
153 if (EINVAL == errno) { // invalid == access denied for some reason | |
154 free(procargs); | |
155 return ARGS_ACCESS_DENIED; /* Insufficient privileges */ | |
156 } | |
157 | |
158 PyErr_SetFromErrno(PyExc_OSError); | |
159 free(procargs); | |
160 return errno; | |
161 } | |
162 | |
163 // copy the number of argument to nargs | |
164 memcpy(&nargs, procargs, sizeof(nargs)); | |
165 iter_args = procargs + sizeof(nargs); | |
166 end_args = &procargs[size]; // end of the argument space | |
167 if (iter_args >= end_args) { | |
168 err = "getcmdargs(): argument length mismatch"; | |
169 goto ERROR_RETURN; | |
170 } | |
171 | |
172 // Save the path | |
173 if (NULL != exec_path) { | |
174 *exec_path = Py_BuildValue("s", iter_args); | |
175 if (*exec_path == NULL) { | |
176 err = "getcmdargs(): exec_path exception"; | |
177 goto ERROR_RETURN; | |
178 } | |
179 } | |
180 | |
181 //TODO: save the environment variables to envlist as well | |
182 // Skip over the exec_path and '\0' characters. | |
183 while (iter_args < end_args && *iter_args != '\0') { iter_args++; } | |
184 while (iter_args < end_args && *iter_args == '\0') { iter_args++; } | |
185 | |
186 /* Iterate through the '\0'-terminated strings and add each string | |
187 * to the Python List arglist as a Python string. | |
188 * Stop when nargs strings have been extracted. That should be all | |
189 * the arguments. The rest of the strings will be environment | |
190 * strings for the command. | |
191 */ | |
192 curr_arg = iter_args; | |
193 start_args = iter_args; //reset start position to beginning of cmdline | |
194 while (iter_args < end_args && nargs > 0) { | |
195 if (*iter_args++ == '\0') { | |
196 /* Fetch next argument */ | |
197 arg = Py_BuildValue("s", curr_arg); | |
198 if (arg == NULL) { | |
199 err = "getcmdargs(): exception building argument string"; | |
200 goto ERROR_RETURN; | |
201 } | |
202 PyList_Append(*arglist, arg); | |
203 Py_DECREF(arg); | |
204 | |
205 curr_arg = iter_args; | |
206 nargs--; | |
207 } | |
208 } | |
209 | |
210 /* | |
211 * curr_arg position should be further than the start of the argspace | |
212 * and number of arguments should be 0 after iterating above. Otherwise | |
213 * we had an empty argument space or a missing terminating \0 etc. | |
214 */ | |
215 if (curr_arg == start_args || nargs > 0) { | |
216 err = "getcmdargs(): argument parsing failed"; | |
217 goto ERROR_RETURN; | |
218 } | |
219 | |
220 ERROR_RETURN: | |
221 // Clean up. | |
222 if (NULL != procargs) { | |
223 free(procargs); | |
224 } | |
225 if (NULL != err) { | |
226 PyErr_SetString(PyExc_SystemError, err); | |
227 return -1; | |
228 } | 135 } |
229 return 0; | 136 return 0; |
230 } | 137 } |
231 | 138 |
232 | 139 |
233 /* return process args as a python list */ | 140 /* return process args as a python list */ |
234 PyObject* | 141 PyObject* |
235 get_arg_list(long pid) | 142 get_arg_list(long pid) |
236 { | 143 { |
237 int r; | 144 int mib[3]; |
238 PyObject *argList; | 145 int nargs; |
239 PyObject *env = NULL; | 146 int len; |
240 PyObject *args = NULL; | 147 char *procargs; |
241 PyObject *exec_path = NULL; | 148 char *arg_ptr; |
| 149 char *arg_end; |
| 150 char *curr_arg; |
| 151 size_t argmax; |
| 152 PyObject *arg = NULL; |
| 153 PyObject *arglist = NULL; |
242 | 154 |
243 //special case for PID 0 (kernel_task) where cmdline cannot be fetched | 155 //special case for PID 0 (kernel_task) where cmdline cannot be fetched |
244 if (pid == 0) { | 156 if (pid == 0) { |
245 return Py_BuildValue("[]"); | 157 return Py_BuildValue("[]"); |
246 } | 158 } |
247 | 159 |
248 /* Fetch the command-line arguments and environment variables */ | 160 /* read argmax and allocate memory for argument space. */ |
249 //printf("pid: %ld\n", pid); | 161 argmax = get_argmax(); |
250 if (pid < 0 || pid > (long)INT_MAX) { | 162 if (! argmax) { return PyErr_SetFromErrno(PyExc_OSError); } |
251 return Py_BuildValue(""); | 163 |
| 164 procargs = (char *)malloc(argmax); |
| 165 if (NULL == procargs) { |
| 166 return PyErr_SetFromErrno(PyExc_OSError); |
252 } | 167 } |
253 | 168 |
254 r = getcmdargs(pid, &exec_path, &env, &args); | 169 /* read argument space */ |
255 if (r == 0) { | 170 mib[0] = CTL_KERN; |
256 //PySequence_Tuple(args); | 171 mib[1] = KERN_PROCARGS2; |
257 argList = PySequence_List(args); | 172 mib[2] = pid; |
258 } | 173 if (sysctl(mib, 3, procargs, &argmax, NULL, 0) < 0) { |
259 else if (r == ARGS_ACCESS_DENIED) { //-2 | 174 if (EINVAL == errno) { // invalid == access denied OR nonexistent PID |
260 argList = Py_BuildValue("[]"); | 175 if ( pid_exists(pid) ) { |
261 } | 176 AccessDenied(); |
262 else { | 177 } else { |
263 argList = Py_BuildValue(""); | 178 NoSuchProcess(); |
| 179 } |
| 180 } |
| 181 free(procargs); |
| 182 return NULL; |
264 } | 183 } |
265 | 184 |
266 Py_XDECREF(args); | 185 arg_end = &procargs[argmax]; |
267 Py_XDECREF(exec_path); | 186 /* copy the number of arguments to nargs */ |
268 Py_XDECREF(env); | 187 memcpy(&nargs, procargs, sizeof(nargs)); |
269 return argList; | 188 |
| 189 arg_ptr = procargs + sizeof(nargs); |
| 190 len = strlen(arg_ptr); |
| 191 arg_ptr += len + 1; |
| 192 |
| 193 if (arg_ptr == arg_end) { |
| 194 free(procargs); |
| 195 return Py_BuildValue("[]"); |
| 196 } |
| 197 |
| 198 // skip ahead to the first argument |
| 199 for (; arg_ptr < arg_end; arg_ptr++) { |
| 200 if (*arg_ptr != '\0') { |
| 201 break; |
| 202 } |
| 203 } |
| 204 |
| 205 /* iterate through arguments */ |
| 206 curr_arg = arg_ptr; |
| 207 arglist = Py_BuildValue("[]"); |
| 208 while (arg_ptr < arg_end && nargs > 0) { |
| 209 if (*arg_ptr++ == '\0') { |
| 210 arg = Py_BuildValue("s", curr_arg); |
| 211 if (NULL == arg) { |
| 212 return NULL; |
| 213 } |
| 214 PyList_Append(arglist, arg); |
| 215 Py_DECREF(arg); |
| 216 // iterate to next arg and decrement # of args |
| 217 curr_arg = arg_ptr; |
| 218 nargs--; |
| 219 } |
| 220 } |
| 221 |
| 222 free(procargs); |
| 223 return arglist; |
270 } | 224 } |
271 | 225 |
272 | 226 |
273 int | 227 int |
274 get_kinfo_proc(pid_t pid, struct kinfo_proc *kp) | 228 get_kinfo_proc(pid_t pid, struct kinfo_proc *kp) |
275 { | 229 { |
276 int mib[4]; | 230 int mib[4]; |
277 size_t len; | 231 size_t len; |
278 mib[0] = CTL_KERN; | 232 mib[0] = CTL_KERN; |
279 mib[1] = KERN_PROC; | 233 mib[1] = KERN_PROC; |
280 mib[2] = KERN_PROC_PID; | 234 mib[2] = KERN_PROC_PID; |
281 mib[3] = pid; | 235 mib[3] = pid; |
282 | 236 |
283 // fetch the info with sysctl() | 237 // fetch the info with sysctl() |
284 len = sizeof(struct kinfo_proc); | 238 len = sizeof(struct kinfo_proc); |
285 | 239 |
286 // now read the data from sysctl | 240 // now read the data from sysctl |
287 if (sysctl(mib, 4, kp, &len, NULL, 0) == -1) { | 241 if (sysctl(mib, 4, kp, &len, NULL, 0) == -1) { |
288 // raise an exception and throw errno as the error | 242 // raise an exception and throw errno as the error |
289 PyErr_SetFromErrno(PyExc_OSError); | 243 PyErr_SetFromErrno(PyExc_OSError); |
290 } | 244 } |
291 | 245 |
292 /* | 246 /* |
293 * sysctl succeeds but len is zero, happens when process has gone away | 247 * sysctl succeeds but len is zero, happens when process has gone away |
294 */ | 248 */ |
295 if (len == 0) { | 249 if (len == 0) { |
296 errno = ESRCH; | 250 NoSuchProcess(); |
297 PyErr_SetFromErrno(PyExc_OSError); | |
298 return -1; | 251 return -1; |
299 } | 252 } |
300 return 0; | 253 return 0; |
301 } | 254 } |
OLD | NEW |